使用Guice + Kotlin绑定对象列表

时间:2016-04-14 13:49:24

标签: dependency-injection guice kotlin

我使用以下控制器定义在Kotlin中编写JavaFX应用程序:

class MainController {

    @Inject private lateinit var componentDescriptors: List<ComponentDescriptor>
    /* More code goes here */

}

我使用Guice进行依赖管理。我试图注入通过java.util.ServiceLoader加载的类实例列表。我的问题是定义一个绑定,它将加载的对象实例列表注入声明的字段。我尝试了基于注释的配置:

internal class MyModule: AbstractModule() {

    override fun configure() { }

    @Provides @Singleton
    fun bindComponentDescriptors(): List<ComponentDescriptor> = 
            ServiceLoader.load(ComponentDescriptor::class.java).toList()

}

和multibinding扩展(在corse的字段定义中将列表切换到Set):

internal class MyModule: AbstractModule() {

    override fun configure() {
        val componentDescriptorBinder = Multibinder.newSetBinder(binder(), ComponentDescriptor::class.java)
        ServiceLoader.load(ComponentDescriptor::class.java).forEach {
            componentDescriptorBinder.addBinding().toInstance(it)
        }
    }

}

但这两种方法都会导致同样的错误:

No implementation for java.util.List<? extends simpleApp.ComponentDescriptor> was bound.
  while locating java.util.List<? extends simpleApp.ComponentDescriptor>
    for field at simpleApp.MainController.componentDescryptors(MainController.kt:6)
  while locating simpleApp.MainController

1 error
    at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1042)
    at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1001)
    at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1051)
    at com.gluonhq.ignite.guice.GuiceContext.getInstance(GuiceContext.java:46)
    at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:929)
    at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:971)
    at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:220)
    at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:744)
    at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2707)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2527)
    ... 12 more

我开始怀疑它与Kotlin gerenic variance和Guice严格类型检查有某种关系。但我不知道如何宣布绑定,因此Guice会知道该注入该字段的内容。

3 个答案:

答案 0 :(得分:17)

是的,它是因为差异而发生的,但有一种方法可以使它发挥作用。

class MainController {
    @JvmSuppressWildcards
    @Inject
    private lateinit var componentDescriptors: List<ComponentDescriptor>    
}

默认情况下,Kotlin为List<? extends ComponentDescriptor>字段生成componentDescriptors签名。 @JvmSuppressWildcards使其生成一个简单的参数化签名List<ComponentDescriptor>

答案 1 :(得分:3)

@Michael给出了正确的answer和解释。这是一个单独测试Set多重绑定策略的示例,用于那些喜欢测试模块的人:

class MyModuleTest {

  @JvmSuppressWildcards
  @Inject
  private lateinit var myTypes: Set<MyType>

  @Before fun before() {
    val injector = Guice.createInjector(MyModule())
    injector.injectMembers(this)
  }

  @Test fun multibindings() {
    assertNotNull(myTypes)
    assertTrue(myTypes.iterator().next() is MyType)
  }
}

答案 2 :(得分:1)

@Michael评论正在起作用。如果要在构造函数中进行注入,则需要执行

之类的操作
class MainController @Inject consturctor(
    private var componentDescriptors: List<@JvmSuppressWildcards ComponentDescriptor>  
) {}