Kotlin + Guice Example
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
- We use local function to configure injections. It is not necessary but allow some nice code grouping.
- Return type of function configureInjections is inferred from it's body (call to finction injector - root of our DSL which we will define soon)
- 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>())
- Please read about extension functions in Kotlin documentation
- Note in variance used in function 'to'. Please read about generics and variance in Kotlin documentation. It is really interesting
- 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).
- 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
- we create ModuleCollector
- we use it as receiver to call given configuration function
- 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!




Comments
Arnaud Des_vosges replied on Sat, 2012/02/04 - 2:08pm
val collected = ArrayList<Module> ()
Maybe it's a detail, but I think we have lost something compared to traditional java code:
List<Module> collected = new ArrayList<Module>()
...
We have lost the explicit information that the code that follows doesn't depend on ArrayList (which is just an implementation choice), but can work with any List<Module>.
(Same problem in usual Scala examples.)
It would like a language that can handle this kind of code:
List<Module> collected = new _() // choose automatically a default impl for List
Alex Tkachman replied on Sat, 2012/02/04 - 2:50pm
in response to:
Arnaud Des_vosges
Arnaud Des_vosges replied on Sat, 2012/02/04 - 3:31pm
in response to:
Alex Tkachman
Ok thanks.
That's similar to what a Scala developer told me.
The problem (for me) is that it seems nobody is using this pattern. :)
So with String specification, would it look like:
val collected : List<String> = ArrayList<> ()
Is there a "diamond" too ?
Anton Arhipov replied on Sun, 2012/02/05 - 5:17am
Alex Tkachman replied on Mon, 2012/02/06 - 2:47am
in response to:
Anton Arhipov
Ladislav Thon replied on Mon, 2012/02/06 - 8:49am
Thanks for the article. This really starts to show Kotlin's nature, which can't be seen in those tiny examples on Kotlin wiki. And sadly, I don't like it. Seriously,
fun injector(config: ModuleCollector.() -> Any?) : Injectoris pretty horrible function signature. Yeah, it's highly subjective, I know :-)Ingo Kegel replied on Mon, 2012/02/06 - 10:41am
in response to:
Ladislav Thon
In all fairness, this article shows something that is not possible in Java at all, namely defining a statically typed DSL. This is a complex task and the corresponding code is necessarily somewhat complex, otherwise it would just be built-in magic.
The signature you compain about takes a function of type () -> Any? (no arguments to anything, even nullable) as an argument and that function is an extension function with a receiver type of ModuleCollector. Both concepts are not present in Java, and I don't see how they could be expressed much better.
If you compared Java code to its equivalent Kotlin code, the Kotlin version would look a lot nicer and much less verbose.
Kookee Gacho replied on Mon, 2012/06/18 - 5:30am
Liezel Jandayan replied on Wed, 2013/01/23 - 7:02am
What is worse is that I now need to manually construct the pull command in the command line, whereas GitHub previously offered the option to generate that for me, which I liked much more.-Donald Leon Farrow