Get Type of a Generic Parameter in Java With Reflection

Get type of a generic parameter in Java with reflection

One construct, I once stumbled upon looked like

Class<T> persistentClass = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];

So there seems to be some reflection-magic around that I unfortunetly don't fully understand... Sorry.

Java generics, get Class<T> of generic parameter

The typesafe, but boilerplatey way to do this is to pass the Class<T> token "where the compiler can see it":

public abstract class RootProcessor<T> {
Class<T> clazz;

protected RootProcessor<T>(Class<T> clazz) {
this.clazz = clazz;
}
}

public class FooProcessor extends RootProcessor<Foo> {
public FooProcessor() {
super(Foo.class);
}
}

If you're doing an unchecked cast but you "know what you're doing" and want the compiler to stop complaining, the correct approach would be localising the non-type-safe-but-you-know-they-work bits and using @SuppressWarnings:

public abstract class RootProcessor<T> {
Class<T> clazz;
{ initClazz(); }

@SuppressWarnings("unchecked")
private void initClazz() {
// the usual verbiage you already have in your question
this.clazz = this.getClass().getGenericSuperclass().yadda().blah();
}
}

(I won't hold this against you :P)

Java reflection - Get actual type of T in a generic interface<T>

Close, but you need to use getGenericParameterTypes, as well as getDeclaredConstructors (since your constructor is not public):

Class<?> cls = (Class<?>) ((ParameterizedType) myClazz.class.getDeclaredConstructors()[0]
.getGenericParameterTypes()[0]) // first constructor, first parameter
.getActualTypeArguments()[0]; // first type argument

System.out.println(cls); // prints 'class otherClazz`

It should be noted that this code will only work if the type argument of the parameter is a concrete type, otherwise the cast to Class<?> will not work. For instance, in the case that the argument is a type variable, getActualTypeArguments()[0] will return an instance of TypeVariable instead of Class<...>.

Java reflection: getting correct type of parameterized generic superclass method

I'd recommend using Guava for this. For example:

Type returnType =
new TypeToken<Derived>() {}
.resolveType(Base.class.getMethod("getE").getGenericReturnType());

See TypeToken.resolveType(Type).

The alternative is pretty complicated, but it's possible. You need to walk the hierarchy and map type variables to type arguments yourself.

Handling the most trivial case would be something like this:

static Type getArgument(TypeVariable<?>   var,
ParameterizedType actual) {
GenericDeclaration decl = var.getGenericDeclaration();
if (decl != actual.getRawType())
throw new IllegalArgumentException();
TypeVariable<?>[] vars = decl.getTypeParameters();
for (int i = 0; i < vars.length; ++i) {
if (var == vars[i]) {
return actual.getActualTypeArguments()[i];
}
}
return null;
}

That sort of simplistic method will fail if you had something like this:

abstract class AbstractDerived<T> extends Base<T> {}
class Derived extends AbstractDerived<String> {}

In cases like that you need to first map E from Base to T from AbstractDerived and then map T to String, so the method has to recurse or iterate the supertypes. I have a more complicated example of something like this here, but that example is still wrong for a number of reasons.

Another hurdle you will run in to is that to return a new type with type variables replaced, you need to implement ParameterizedType, GenericArrayType and WildcardType yourself, or else use the undocumented sun.reflect classes.

All of that is to say you should really just use Guava which already handles that stuff, unless you're doing something like writing your own TypeToken for some reason.

The caveat to all of this, which it seems like you already know, is that all of this depends on an actual type argument being provided to the supertype in an extends clause (explicit or implicit as in an anonymous class). If you just do new Base<Double>() there's no way to recover the type argument at runtime.

Reflection for Class of generic parameter in Java?

This can be achieved with reflection only because you explicitly used String, otherwise this information would've been lost due to type erasure.

ParameterizedType t = (ParameterizedType) MyClass.class.getGenericSuperclass(); // OtherClass<String>
Class<?> clazz = (Class<?>) t.getActualTypeArguments()[0]; // Class<String>

Get Class-object representation of generic parameter in Java

The reason why

Class<T> persistentClass = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];

works was because the superclass of this happens to be a class with a parameterised type as its superclass. Therefore you can get the actual type arguments of that type. The type parameters of superclasses are stored as metadata in the class file if you write them in the source file.

However, in your case, whatever is passed to the onCatch parameter is not going to have a superclass of Consumer<T>. After all, Consumer<T> is not a class! You need to use getGenericInterfaces and find the one that has the name that starts with java.util.function.Consumer.

System.out.println(
// I've assumed the Consumer interface is the first one, to keep it brief
((ParameterizedType)onCatch.getClass().getGenericInterfaces()[0])
.getActualTypeArguments()[0]
);

This would work if the caller calls onCatch like this:

onCatch(new Consumer<RuntimeException>() {
@Override
public void accept(RuntimeException e) {

}
});

The anonymous class will implement Consumer<RuntimeException>, and this information will be written to the class file representing the anonymous class.

However, if you use a lambda:

onCatch((RuntimeException e) -> {});

Then only a method like this is generated in the same class as the caller:

private static void lambda$caller$0(RuntimeException e) {

}

and at runtime, invokedynamic is used to create an instance that implements Consumer, and this is the bad news: the type parameter RuntimeException is not part of the generated class for this instance, for whatever reason.

The only way you can find RuntimeException now then, is if you somehow know who the caller is, and find the lambda$caller$0 method, and look at its parameter.

That said, everything I've wrote so far is pretty much all implementation detail, and I wouldn't use any of that in production code. I would say you should just add a Class<E> parameter:

onCatch(RuntimeException.class, e -> {});

It doesn't look that different on the caller's side anyway.

How can I obtain the type parameter of a generic interface from an implementing class?

Resolve the type of T by the generic interface. E.g.

public interface SomeInterface<T> {
}

public class SomeImplementation implements SomeInterface<String> {

public Class getGenericInterfaceType(){
Class clazz = getClass();
ParameterizedType parameterizedType = (ParameterizedType) clazz.getGenericInterfaces()[0];
Type[] typeArguments = parameterizedType.getActualTypeArguments();
Class<?> typeArgument = (Class<?>) typeArguments[0];
return typeArgument;
}
}

public static void main(String[] args) {
SomeImplementation someImplementation = new SomeImplementation();
System.out.println(someImplementation.getGenericInterfaceType());
}

PS: Keep in mind that the acutalTypeArguments are of type Type. They must not be a Class. In your case it is a Class because your type definition is EventHandler<MyEvent>.

Java generics - obtaining actual type of generic parameter

The code you use only works in some very specific cases, where the actual type parameter is known (and stored) at compile time.

For example if you did this:

class IntegerList extends ArrayList<Integer> {}

List<Integer> l = new IntegerList();

In this case the code you showed would actually return Integer.class, because Integer is "baked into" the IntegerList.

Some libraries (ab)use this trick via the use of type tokens. See for example the GSON class TypeToken:

Represents a generic type T. You can use this class to get the generic type for a class. > For example, to get the generic type for Collection<Foo>, you can use:

Type typeOfCollectionOfFoo = new TypeToken<Collection<Foo>>(){}.getType()

This works because the anonymous class created in here has compiled-in the information that its type parameter is Collection<Foo>.

Note that this would not work (even if the TypeToken class wouldn't prevent it by making its constructor protected):

Type typeOfCollectionOfFoo = new TypeToken<Collection<Foo>>().getType()


Related Topics



Leave a reply



Submit