How can I inject for generic inherited types?

244 views Asked by At

I spent a plenty of time for finding any answers, but I think I have to ask.

I'm using Weld-SE for testing my entities.

I prepared entity randomizer for testing.

abstract class BaseEntityRandomizer<T extends BaseEntity>
        implements Randomizer<T> {

    @Override public T getRandomValue() {
        ...
    }
}

class MySomeOtherEntityRandomizer
        extends BaseEntityRandomizer<MySomeOther> {

    @Override public MySomeOther getRandomValue() {
        ...
    }
}

Now, with my test class, I want to inject those randomizers which each matches generic parameters

@ExtendWith(WeldJunit5Extension.class)
@AddPackages({BaseEntityRandomizer.class})
abstract class BaseEntityTest<T extends BaseEntity> {

    @Test void doSome() {
    }

    @Inject
    private BaseEntityRandomizer<T> entityRandomizer;
}

class MySomeOtherTest extends BaseEntityTest<MySomeOther> {
    ...
    // I expect an instance of MySomeOtherRandomizer in injected
    // into the entityRandomizer field.
}

Subclasses of randomizers and tests are prepared.

But I failed to make it work.

How can I make it work?

I tried with following factory class

class BaseEntityRandomizerFactory {

   @Produces
   public BaseEntityRandomizer<MySome> produceMySomeRandomizer() {
        return new MySomeRandomizer();
   }
}

I got

org.jboss.weld.exceptions.IllegalArgumentException: 
WELD-001408: Unsatisfied dependencies for type BaseEntityRandomizer<T extends BaseEntity> with qualifiers @Default
  at injection point [BackedAnnotatedField] @Inject protected transient ....BaseEntityTest.entityRandomizer
  at ....BaseEntityTest.entityRandomizer(BaseEntityTest.java:0)
1

There are 1 answers

0
Siliarus On

One way to achieve this is to use CDI Programmatic lookup. In your case, I'd start with @Inject Instance<Object> and then you can use subsequent calls to select() and get() methods to pick up whichever bean you desire. Usage looks something like this (assumes existence of beans with types Foo, Bar and List<String>):

    @Inject
    private Instance<Object> instance;
    
    @Test void doSome() {
      // selecting and obtaining instances of beans
      Foo foo = entityRandomizer.select(Foo.class).get();
      Bar bar = entityRandomizer.select(Bar.class).get();
      // in case you need to select a parameterized type from instance, use TypeLiteral
      List<String> listBean = entityRandomized..select( new TypeLiteral<List<String>>(){}).get()
    }