Improvement on Guice ... bind(javaClass<EmailSender>()).to(javaClass<DummyEmailSender>())


#1

So with a Guice binding example of:

 
bind(javaClass<EmailSender>()).to(javaClass<DummyEmailSender>())

Is there a better syntax alternative?  I’ve tried adding a helper function that takes KClass but not progressing that (EmailSender::class.javaClass is not EmailSender.class so I’m not sure how to progress that yet).

Has anyone got some good thoughts?

Cheers, Rob.


#2

You can add reified inline function to help with syntax:

inline fun <reified T> Binder.bind() = bind(javaClass<T>())
inline fun <reified T> Binding.to() = to(javaClass<T>())

and then use it like this:

binder.bind<EmailSender>().to<DummyEmailSender>()

PS: I don’t know Guice very well, so I may be choosing wrong receiver types for extension functions.


#3

If you want to get a java.lang.Class instance by a KClass instance, you can use "java" extension property (defined in kotlin.reflect.jvm): EmailSender::class.java


#4

Thanks IIya,

That does compile but it does give me a VerifyError when I run.  If you are keen you can git clone from https://github.com/avaje-metric/example-app

I’m using Java build 1.8.0_45-b14, Kotlin 0.12.213

In case it is interesting the error I see is:

00:07:21.700 [org.example.main.MainPackage.main()] DEBUG o.example.myapp.web.module.WebModule - configuring module …
00:07:21.709 [org.example.main.MainPackage.main()] WARN  o.e.j.u.component.AbstractLifeCycle - FAILED o.e.j.w.WebAppContext@5866c318{/,file:/home/rob/work-avaje/example-app/src/main/webapp/,STARTING}: java.lang.VerifyError: Bad access to protected data in invokevirtual
Exception Details:
  Location:
  org/example/myapp/service/module/AppServiceModule.bind(Lcom/google/inject/AbstractModule;)Lcom/google/inject/binder/AnnotatedBindingBuilder; @14: invokevirtual
  Reason:
  Type ‘com/google/inject/AbstractModule’ (current frame, stack[0]) is not assignable to ‘org/example/myapp/service/module/AppServiceModule’
  Current Frame:
  bci: @14
  flags: { }
  locals: { ‘org/example/myapp/service/module/AppServiceModule’, ‘com/google/inject/AbstractModule’ }
  stack: { ‘com/google/inject/AbstractModule’, ‘java/lang/Class’ }
  Bytecode:
  0x0000000: 2b12 11b8 0017 2b12 19b8 001d 121f b600
  0x0000010: 22b0   


#5

Thanks Alexander, yes I like that better.  With the syntax highlighting of "::class" and ".java" in Idea it's much more readable than what I had before.

Example:

bind(MetricService::class.java).asEagerSingleton()
bind(EmailSender::class.java).to(DummyEmailSender::class.java)

Thanks, Rob.


#6

We'll take a look at VerifyError, it looks like Kotlin confused two receivers - instance and extension. Meanwhile, try moving these two functions out of class so they become top-level extension functions.


#7

I've reproduced VerifyError, here is an issue: https://youtrack.jetbrains.com/issue/KT-7971

Unfortunately, my previous idea won’t work, because “bind” is protected in AbstractModule :frowning:


#8

Great, thanks for that.  I've given the ticket a vote :)