Laird Nelson Laird Nelson - 1 month ago 15
Java Question

What type literal must I use to have CDI's Instance::select method work properly?

Suppose I have an interface like this:

public interface Converter<T> { /*...*/ }


And suppose in a CDI environment I have successfully done this:

@Inject
@Any
private Instance<Converter<?>> converters;


(By "successfully" I mean that I can do the following and see several converters in the output, so beans are being discovered and supplied properly:

for (final Object o : converters) {
System.out.println("*** converter: " + o);
}


…so bean discovery is not the issue.)

And now suppose that given
Integer.class
, I'd like to do this:

final TypeLiteral<Converter<Integer>> typeLiteral = new TypeLiteral<Converter<Integer>>(){};
final Instance<Converter<Integer>> subInstance = converters.select(typeLiteral);
final Converter<Integer> converter = subInstance.get();


This works fine.

Now, in my actual code,
Integer.class
is passed in, as a value fulfilling a parameter declared as
Class<T>
, so what I really have is this:

final TypeLiteral<Converter<T>> typeLiteral = new TypeLiteral<Converter<T>>(){};
final Instance<Converter<T>> subInstance = converters.select(typeLiteral);
final Converter<T> converter = subInstance.get(); // this does not work


The
get()
call fails with a stack trace that starts with something that looks like the following:

org.jboss.weld.exceptions.UnsatisfiedResolutionException: WELD-001334: Unsatisfied dependencies for type Converter<T> with qualifiers @Any
at org.jboss.weld.bean.builtin.InstanceImpl.get(InstanceImpl.java:105)


What must I do to make this selection succeed?

One thing I notice is that the stack is reporting that a
Converter<T>
cannot be found. This looks suspicious: I would have expected it to talk in terms of
Converter<Integer>
instead, since the
T
"slot" is being "filled" with
Integer.class
at runtime, although, to be fair, I did indeed supply a
new TypeLiteral<Converter<T>>(){}
, not
new TypeLiteral<Converter<Integer>>(){}
.

Anyway, all this tells me that
TypeLiteral<T>
is using
T
as the type to look for, not the actual value "filling" the
T
"slot", and indeed, there is no converter declared as
implements Converter<T>
, only a converter declared as
implements Converter<Integer>
, and so I'm worried that what I'd like to do here is fundamentally impossible.

Answer

Creating a TypeLiteral to capture generic parameters only works if those parameters are known at compile time, so new TypeLiteral<Converter<Integer>>(){}.

If the type parameter is not known at compile time then a TypeLiteral cannot capture the parameter information because that information has been removed due to type erasure. So creating a new TypeLiteral<Converter<T>>(){} actually just creates a new TypeLiteral<Converter<object>>(){}.

This means your select(typeLiteral) will not work as expected since it will receive the type literal for Converter<object>.