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 forCollection<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
How to Tell Jackson to Ignore a Field During Serialization If Its Value Is Null
Java - Get Pixel Array from Image
Calculating Days Between Two Dates With Java
How to Refer to the Current Type With a Type Variable
Bufferedwriter Not Writing Everything to Its Output File
Why Is There No Sortedlist in Java
How to Iterate Over a Jsonobject
Value Change Listener to Jtextfield
No Persistence Provider For Entitymanager Named
How to Generate Exceptions from Repaintmanager
How to Save a String to a Text File Using Java
Get Type of a Generic Parameter in Java With Reflection
Get a List of Resources from Classpath Directory
Why in Java 8 Split Sometimes Removes Empty Strings At Start of Result Array
Simpledateformat Ignoring Month When Parsing
Standard Concise Way to Copy a File in Java