Search code examples
javakotlindependency-injectionguice

Generic Guice Module


Is it possible to define a generic Guice-Module?

I always get a CreationException:

Exception in thread "main" com.google.inject.CreationException: Unable to create injector, see the following errors:

1) com.example.test.GenericClass<T> cannot be used as a key; It is not fully specified.

1 error
    at com.google.inject.internal.Errors.throwCreationExceptionIfErrorsExist(Errors.java:470)
    at com.google.inject.internal.InternalInjectorCreator.initializeStatically(InternalInjectorCreator.java:155)
    at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:107)
    at com.google.inject.Guice.createInjector(Guice.java:99)
    at com.google.inject.Guice.createInjector(Guice.java:73)
    at com.google.inject.Guice.createInjector(Guice.java:62)
    at com.example.test.GenericModuleKt.main(genericModule.kt:23)
    at com.example.test.GenericModuleKt.main(genericModule.kt)

This is my example Code:

package com.example.test

import com.google.inject.AbstractModule
import com.google.inject.Guice
import com.google.inject.Key
import com.google.inject.TypeLiteral

class SomeClass

class GenericClass<T>

class GenericModule<T> : AbstractModule() {
    override fun configure() {
        bind(object : TypeLiteral<GenericClass<T>>(){})
    }
}

fun main() {
    val injector = Guice.createInjector(
        GenericModule<SomeClass>()
    )

    val instance : GenericClass<SomeClass> =
        injector.getInstance(object : Key<GenericClass<SomeClass>>(){})
}

EDIT: Working example with the help of Yurii Melnychuk

You need to use Types.newParameterizedType to specify the generic class:

class SomeClass

class GenericClass<T>

class GenericModule(
    private val genericClass: Class<*>
) : AbstractModule() {
    override fun configure() {
        val paraType = Types.newParameterizedType(GenericClass::class.java, genericClass)
        bind(TypeLiteral.get(paraType))
    }
}

fun main() {
    val injector = Guice.createInjector(
        GenericModule(SomeClass::class.java)
    )

    val instance : GenericClass<SomeClass> =
        injector.getInstance(object : Key<GenericClass<SomeClass>>(){})
}

If you head into any problem (especially when using generic interfaces) try using Java instead of Kotlin. This solved some problems for me.


Solution

  • Generic type info is erased at runtime so it is not possible to do what you want the way you do it (at least in Java, Kotlin maybe different in that regard, but I am not familiar with it, maybe reified generics can help).

    But from Java perspective I would rather just pass TypeLiteral/Type to module as constructor argument and bind it (com.google.inject.util.Types#newParameterizedType should help).