Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dagger 2 Generic Type class inject error

I'm not able to let MyClass here being injected due to its Generic nature.

Dagger complains with this error:

Error:(187, 10) error: com.test.MyClass has type parameters, cannot members inject the raw type. via: com.test.MyComponent.inject(com.test.MyClass obj) [component injection method for type: com.test.MyClass]

I googled a bit but was unable to find a solution to this case scenario.

class MyClass<Type> {
    @Inject
    UserCredentials userCredentials;

    ResultProducer<Type> mRP;

    public MyClass(ResultProducer<Type> resultProd) {
        mRP = resultProd;
        Injector.getComponent().inject(this);
    }

    public Type getResult() {
        if (userCredentials.isLoggedIn()) {
            mRP.get();
        } else {
            mRP.getAnonymousCache();
        }
    }
}

@Component(modules = CredentialsModule.class )
interface MyComponent {
    void inject(MyClass obj);
}

@Module
class CredentialsModule {
    @Provides
    public UserCredentials provideUserCredentials() {
        return new UserCredentials();
    }
}
like image 532
Andrea Baccega Avatar asked Sep 10 '15 10:09

Andrea Baccega


4 Answers

I have run into the same issue and found this article.

In a nutshell you have this options:

  1. Make not generic wrapper class containing injected fields, make it to be a field of your class and inject it instead of generic class itself.
  2. Inject child non-generic class instead of base class. All annotated with @Inject fields of base class will be also injected, but they have to be public/protected that is not good.
  3. Make annotated with @Inject setter in the base class and private field. Injecting child non-generic class will lead to triggering the setter with parameter got from the object graph.
like image 153
lobzik Avatar answered Oct 15 '22 11:10

lobzik


For many will be useful to know, that it's allowed to inject fields into a generic class, without injecting the class itself.
It will be injected for you when a subclass is injected.
So you can have:

abstract class MyClass<? extends Something> {
    @Inject UserCredentials userCredentials;
    // you can use it further
}

class AnotherClass extends MyClass<Whatever> {
    // just inject this one
}
like image 40
Leonid Ustenko Avatar answered Oct 15 '22 09:10

Leonid Ustenko


You can use dagger2 without "inject".

Add method to your component:

@Component(modules = CredentialsModule.class )
interface MyComponent {
void inject(MyClass obj);

UserCredentials getUserCredentials();
}

And use it without any problems:

userCredentials=Injector.getComponent().getUserCredentials();

But this approach can be inconvenient if you have a lot of fields to inject

like image 39
alexshr Avatar answered Oct 15 '22 10:10

alexshr


I solved this issue a bit differently, like so (we're 5 years further, so its in Kotlin, but i guess Java would work too):

// The class I want to inject, multiple generics
class SomeDataStorage<K, V> {

  @Inject
  lateinit var theDependencyINeed: TheDependencyINeed

  init {
    ComponentSingleton.getSomeComponent().inject(this as SomeDataStorage<Any, Any>)
  }

  fun getValue(key: K): V {
    return theDependencyINeed.doSomeStuff() as V
  }
}

// Component
interface MyComponent {
  fun inject(someDataStorage: SomeDataStorage<Any, Any>)
}

The trick is to get some common parent of the generics you allow (in my case i dont care, so i just do Any, which is everything) and use that. Some unchecked casts are required, but thats OK.

like image 45
Hylke Avatar answered Oct 15 '22 10:10

Hylke