DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Metal and the Simulated Annealing Algorithm
  • Reactive Kafka With Spring Boot
  • Why You Should Migrate Microservices From Java to Kotlin: Experience and Insights
  • How To Create a Homescreen Widget in Android

Trending

  • Navigating Double and Triple Extortion Tactics
  • After 9 Years, Microsoft Fulfills This Windows Feature Request
  • Unlocking the Potential of Apache Iceberg: A Comprehensive Analysis
  • Comparing Managed Postgres Options on The Azure Marketplace
  1. DZone
  2. Coding
  3. Languages
  4. Kotlin + Guice Example

Kotlin + Guice Example

By 
Alex Tkachman user avatar
Alex Tkachman
·
Feb. 04, 12 · Interview
Likes (3)
Comment
Save
Tweet
Share
33.0K Views

Join the DZone community and get the full member experience.

Join For Free

While Kotlin programming language prepares for the public early access program I want to share with you one an example how easly it can be used with existing Java codebases.

This short note does not intend to teach the reader how to use Kotlin but only to make him/her intersted to learn.


We will create small toy application using Kotlin and the charming Guice framework.  The application is nothing more than the usual "Hello, World!" but it contains an application service and logging service and each service will have two incarnations - for testing and for production use.

Let us start with an abstract LoggingService. It will be an abstract class with only one abstract method to output the passed string. Most probably the code below does not require much explaination.

abstract class LoggingService() {
    abstract fun info(message: String?)
}

Now we will define a simple implementation, which prints the given message to the standard output

class StdoutLoggingService() : LoggingService(){
    override fun info(message: String?) = println("INFO: $message")
}

override modifier tells the compiler that the method overrides some method of a superclass.

Note also string interpolation

Now we will create a little more sophisticated implementation, which we will use in production :)

class JdkLoggingService [Inject] (protected val jdkLogger: Logger) : LoggingService() {
    override fun info(message: String?)  = jdkLogger.info(message)
}

[Inject] annotation on constructor tells Guice how to create JdkLoggerService.

val on constructor parameter asks to create final property initialized by given value. var asks for non-final one and neither of them define simple constructor parameter

Now we are ready to define the abstract application service and both implementations of it for test and production use.

abstract class AppService(protected val logger: LoggingService, protected val mode: String) {
    fun run() {
        logger.info("Application started in '$mode' mode with ${logger.javaClass.getName()} logger")
        logger.info("Hello, World!")
    }
}

class RealAppService [Inject] (logger: LoggingService) : AppService(logger, "real")

class TestAppService [Inject] (logger: LoggingService) : AppService(logger, "test")

Note how elegantly Kotlin allows us to define properties and pass values to superconstructor 

Now we go to more interesting part - creating min-DSL for Guice. But let us start with how we want our application to look like.

fun main(args: Array<String>) {
    fun configureInjections(test: Boolean) = injector {
        +module {
            if(test) {
                bind<LoggingService>().toInstance (StdoutLoggingService())
                bind<AppService>().to<TestAppService> ()
            }
            else {
                bind<LoggingService>().to<JdkLoggingService>()
                bind<AppService>().to<RealAppService> ()
            }
        }
    }

    configureInjections(test=false).getInstance<AppService> ().run()
}

There are few things to notice here

  1. We use local function to configure injections. It is not necessary but allow some nice code grouping.
  2. Return type of function configureInjections is inferred from it's body (call to finction injector - root of our DSL which we will define soon)
  3. We call configureInjections using named arguments syntax. It is not necessary in our case but very convinient when we have many parameters and some of them are optional.

Now it is time to define simple parts of our DSL. It will be three extension functions for some Guice classes.

fun <T> Binder.bind() = bind(javaClass<T>()).sure()

fun <T> AnnotatedBindingBuilder<in T>.to() = to(javaClass<T>()).sure()

fun <T> Injector.getInstance() = getInstance(javaClass<T>())
  1. Please read about extension functions in Kotlin documentation
  2. Note in variance used in function 'to'. Please read about generics and variance in Kotlin documentation. It is really interesting
  3. javaClass<T>() call is Kotlin standard library for Java (remember that Kotlin can be compiled for other platforms like javascript as well) is equivalent to T.class in Java. The huge difference here is that in Kotlin it applicable not only to real classes but also to generic type parameters (thanks to runtime type information).
  4. Call to method sure() is Kotlin way (soon to be replaced by special language construct) to ensure non-nullability. As Java has not notation of nullable and non-nullable types we need to take some care on integration point. Please read amazing story of null-safety in Kotlin documentation.

Finally we are ready to most complex part - definition of method injector.

fun injector(config: ModuleCollector.() -> Any?) : Injector {
    val collector = ModuleCollector()
    collector.config()
    return Guice.createInjector(collector.collected).sure()
}

It has one parameter of type ModuleCollector.()->Any? which means "extension function for ModuleCollector(we will define ModuleCollector few lines below), which does not accept any parameters and returns value of any type potentially nullable"

The implementation is very straight-forward

  1. we create ModuleCollector
  2. we use it as receiver to call given configuration function
  3. we ask Guice to create Injector for all modules configured (consult Guice documentation for details) 

This is extremely powerful method of defining DSLs in Kotlin

As we saw above we call method injector with function block expression.

The fact that parameter is extension function makes all (modulo visibility rules) methods of ModuleCollector available inside function block.

The last part of our DSL is definition of ModuleCollector.

It contains internal list of collected modules and only two methods

  • method module - uses variation of the same extension-function-as-parameter trick - we use extension function to implement method of anonimous class.
Please consult Kotlin documentation on object expressions
  • method plus - overrides unary plus operation
Please consult Kotlin documentation on operator overloading.

It is very important to note that this method is both extension method and member of class ModuleCollector. It allows very good context catching on call site. In our case above we call this method inside extention function with ModuleCollectoror receiver and apply it to Module created by call to method Module.
class ModuleCollector() {
    val collected = ArrayList<Module> ()

    fun module (config: Binder.()->Any?) : Module = object: Module {
        override fun configure(binder: Binder?) {
            binder.sure().config()
        }
    }

    fun Module.plus() {
        collected.add(this)
    }
}

We are done. I hope that was interesting. This note is a very brief and non-detailed introduction on goodies of Kotlin.  If this has motivated you to read more about Kotlin then my goal is achieved.

Thank you and till next time!

Kotlin (programming language)

Opinions expressed by DZone contributors are their own.

Related

  • Metal and the Simulated Annealing Algorithm
  • Reactive Kafka With Spring Boot
  • Why You Should Migrate Microservices From Java to Kotlin: Experience and Insights
  • How To Create a Homescreen Widget in Android

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!