Search code examples
guiceguice-3

Guice, Injecting TypeLiteral<T> when using @AssistedInject


This works:

public static class SomeGenericType<T> {
    private TypeLiteral<T> type; 

    @Inject
    public SomeGenericType(TypeLiteral<T> type) {
        this.type = type; 
    }

    public Class<? super T> getType() {
        return type.getRawType();
    }
}

Guice automatically injects the TypeLiteral representing String when I do:

@Inject SomeGenericType<String> foo;

But when trying the same thing with Assisted Inject:

public static interface FooFactory<T> {
    Foo<T> create(String name);
}

public static class Foo<T> {

    @AssistedInject
    public Foo(TypeLiteral<T> type, @Assisted String name) {
        ....

My Module looking like this:

public static class TestMod extends AbstractModule {
    @Override
    protected void configure() {
        install(new FactoryModuleBuilder().build(new TypeLiteral<FooFactory<String>>(){}));
    }   
}

I get an exception while installing the module:

TypeLiteral<T> cannot be used as a Key, it is not fully specified. 

It's certainly the TypeLiteral that I'm trying to Inject that is the problem, as the generic factory does work fine when I remove it.

So, I'll probably just roll my own factory for the moment, but I'm curious to whether this should work? Is it a matter of using the FactoryModuleBuilder slightly differently?


Solution

  • How are you accessing the instance of FooFactory? I built the variation on your code below and it worked for me:

    public class AnotherGuiceTest {
        public static void main( String[] args ) {
            Injector i = Guice.createInjector( new TestMod() );
            FooFactory<String> ff = i.getInstance( Key.get( new TypeLiteral<FooFactory<String>>() {} ) );
            ff.create( "myname" );
        }
    }
    
    interface FooFactory<T> {
        Foo<T> create( String name );
    }
    
    class Foo<T> {
    
        @Inject
        public Foo( TypeLiteral<T> type, @Assisted String name ) {
            System.out.println( type.getRawType() );
            System.out.println( name );
        }
    }
    
    class TestMod extends AbstractModule {
        @Override
        protected void configure() {
            install( new FactoryModuleBuilder().build( new TypeLiteral<FooFactory<String>>() {} ) );
        }
    }
    

    Outputs:

    class java.lang.String
    myname
    

    Note I used a regular @Inject annotation rather than @AssistedInject which I think is for multiple constructors in a factory. This also works if you inject the instance directly:

    public class AnotherGuiceTest {
        public static void main( String[] args ) {
            Injector i = Guice.createInjector( new TestMod() );
            AppClass ac = i.getInstance( AppClass.class );
        }
    }
    
    class AppClass {
        @Inject
        public AppClass( FooFactory<String> fooFactory ) {
            fooFactory.create( "test" );
        }
    }
    

    Outputs:

    class java.lang.String
    test