172

Is it possible to get the type of a generic parameter?

An example:

public final class Voodoo {
    public static void chill(List<?> aListWithTypeSpiderMan) {
        // Here I'd like to get the Class-Object 'SpiderMan'
        Class typeOfTheList = ???;
    }

    public static void main(String... args) {
        chill(new ArrayList<SpiderMan>());
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
cimnine
  • 3,987
  • 4
  • 33
  • 49

18 Answers18

176

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.

Aubin
  • 14,617
  • 9
  • 61
  • 84
DerMike
  • 15,594
  • 13
  • 50
  • 63
  • 2
    But declare it as abstract then. when you want to use it, then sublclass it inline. – Snicolas Feb 13 '12 at 14:55
  • This was helpful indeed! I actually ended up writing this one in a loop going up the super class hierarchy cause I was looking for the type of a generic parameter of a specific interface that got implemented by that class – mac Oct 26 '12 at 19:04
  • For example it is not enough to define abstract class since somebod can still write: public create( Class clazz ) { new MyAbstractClass(){}; } – Igor Grinfeld Nov 07 '12 at 14:24
  • This will only work one inheritance level and not 2. I.E. it will work for class Foo extends List, but not for class Bar extends Foo. – Snicolas Dec 20 '12 at 17:34
  • Unfortunately, these days such code elicits unavoidable "unchecked cast" warnings, even though we know converting from Type to Class is perfectly legit. Type erasure kicks us in the nuts again! Still, this is probably the best way to work around not being able to use "T.class" as a type token. – Ti Strga Oct 15 '13 at 18:12
  • 49
    Even though this is very old *and accepted* for some reason, I've downvoted because it simply doesn't answer the question. It would *not* give "SpiderMan" in the example given in the question. It's undoubtedly useful in some situations, but it doesn't work for the question that was asked. – Jon Skeet May 21 '14 at 09:18
  • 1
    This is wird. It will only work if it's used as an annonymous instance, as stated by @Snicolas. –  Aug 28 '14 at 15:40
  • @JonSkeet So what should be answered? – Stephan Dec 30 '14 at 19:17
  • 1
    @Stephan: I'm not sure what you mean. Do you mean "what should be the answer"? If so, it's the various answers that say you just can't do it... – Jon Skeet Dec 30 '14 at 19:20
  • I have to agree with @JonSkeet here. The answers seem to get more and more accurate as I scroll down the page. The correct answer to this question, of course, is "this can't be done". – Dawood ibn Kareem Dec 30 '14 at 21:43
  • 26
    Nobody who's come this far is about to accept "It can't be done" for an answer. We're here because we're desperate. – Addison Apr 07 '15 at 07:15
  • Funny, I come back over and over to this and actually found an example where it exactly applies, as it hierarchy is limited to one level. – Snicolas Apr 22 '15 at 09:10
  • Seems that it doesn't work for level 1 either when your class implements a parametrized interface.. class returned is always java.lang.Object – milosmns May 22 '15 at 12:10
  • 18
    I get this exception : `Exception in thread "main" java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType` not sure what is the constraint . – coretechie Feb 29 '16 at 14:25
  • beautiful! Solved my problem and saved me a lot of time of having to write the same method 40 times on each subclass! – pqsk Apr 03 '16 at 08:01
  • 1
    @JonSkeet I have no idea how this is an accepted answer, and yes the answer is that it can not be done! I downgraded as well. This is no sense. – Mohy Eldeen Sep 08 '16 at 17:44
  • 4
    Like @Dish I just get a `java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType` – Kenny Wyland Oct 08 '16 at 02:44
  • It would appear it is supported now by Guava's TypeToken: https://github.com/google/guava/wiki/ReflectionExplained – Babyburger May 15 '17 at 14:00
  • And you can't use this approach to climb up the class hierarchy using loop. The second iteration will produce java.lang.object instead of actual class. – KMC May 21 '19 at 18:40
  • `java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType` could be solved by adding recursive method `public Constructor() { this.persistentClass = (Class) (getParametrizedType(getClass())) .getActualTypeArguments()[0]; } private ParameterizedType getParametrizedType(Class> clazz){ if(clazz.getSuperclass().equals(Constructor.class)){ return (ParameterizedType) clazz.getGenericSuperclass(); } else { return getParametrizedType(clazz.getSuperclass()); } }` – Vlad Troyan Jul 13 '20 at 17:22
  • 1
    This approach won't work if there are other generic descendants in the chain (see my answer below). There are couple of other cases when it wort either: the people in the comments have pointed them out. – real4x Jul 14 '20 at 07:24
159

I want to try to break down the answer from @DerMike to explain:

First, type erasure does not mean that the JDK eliminates type information at runtime. It's a method for allowing compile-time type checking and runtime type compatibility to coexist in the same language. As this block of code implies, the JDK retains the erased type information--it's just not associated with checked casts and stuff.

Second, this provides generic type information to a generic class exactly one level up the heirarchy from the concrete type being checked--i.e. an abstract parent class with generic type parameters can find the concrete types corresponding to its type parameters for a concrete implementation of itself that directly inherits from it. If this class were non-abstract and instantiated, or the concrete implementation were two levels down, this wouldn't work (although a little bit of jimmying could make it apply to any predetermined number of levels beyond one, or up to the lowest class with X generic type parameters, et cetera).

Anyway, on to the explanation. Here's the code again, separated into lines for ease of reference:

1# Class genericParameter0OfThisClass = 
2#     (Class)
3#         ((ParameterizedType)
4#             getClass()
5#                .getGenericSuperclass())
6#                    .getActualTypeArguments()[0];

Let 'us' be the abstract class with generic types that contains this code. Reading this roughly inside out:

  • Line 4 gets the current concrete class' Class instance. This identifies our immediate descendant's concrete type.
  • Line 5 gets that class' supertype as a Type; this is us. Since we're a parametric type we can safely cast ourselves to ParameterizedType (line 3). The key is that when Java determines this Type object, it uses type information present in the child to associate type information with our type parameters in the new ParameterizedType instance. So now we can access concrete types for our generics.
  • Line 6 gets the array of types mapped into our generics, in order as declared in the class code. For this example we pull out the first parameter. This comes back as a Type.
  • Line 2 casts the final Type returned to a Class. This is safe because we know what types our generic type parameters are able to take and can confirm that they will all be classes (I'm not sure how in Java one would go about getting a generic parameter that doesn't have a Class instance associated with it, actually).

...and that's pretty much it. So we push type info from our own concrete implementation back into ourselves, and use it to access a class handle. we could double up getGenericSuperclass() and go two levels, or eliminate getGenericSuperclass() and get values for ourselves as a concrete type (caveat: I haven't tested these scenarios, they haven't come up for me yet).

It gets tricky if your concrete children are be an arbitrary number of hops away, or if you're concrete and not final, and especially tricky if you expect any of your (variably deep) children to have their own generics. But you can usually design around those considerations, so this gets you most of the way.

Hope this helped someone! I recognize this post is ancient. I'll probably snip this explanation and keep it for other questions.

Stephan
  • 41,764
  • 65
  • 238
  • 329
Mark McKenna
  • 2,857
  • 1
  • 17
  • 17
  • 4
    Just answering your "I'm not sure how in Java one would go about getting a generic parameter that doesn't have a Class instance associated with it, actually)": This is possible if the parameter to the generic class is either a "wildcard expression" (say: ? extends SomeClass) or a "type variable" (say: where T comes from a generic subclass). in both cases the "Type" instances returned can't be cast to "Class" as each VM might have a different implementation for both which implement the Type interface but does not derive directly from java.lang.Class. – Roberto Andrade Jul 15 '13 at 20:15
23

Actually I got this to work. Consider the following snippet:

Method m;
Type[] genericParameterTypes = m.getGenericParameterTypes();
for (int i = 0; i < genericParameterTypes.length; i++) {
     if( genericParameterTypes[i] instanceof ParameterizedType ) {
                Type[] parameters = ((ParameterizedType)genericParameterTypes[i]).getActualTypeArguments();
//parameters[0] contains java.lang.String for method like "method(List<String> value)"

     }
 }

I'm using jdk 1.6

dernasherbrezon
  • 848
  • 7
  • 16
  • 13
    -1: this does not solve the problem in the question; it only gives you the type declared in the method signature, not the actual runtime type. – Michael Borgwardt Aug 31 '11 at 12:03
  • 6
    Might not answer the OP's question, but it's a useful technique. – dnault Sep 12 '13 at 18:26
  • 4
    This is the answer to an entirely different question. – Dawood ibn Kareem Dec 30 '14 at 21:38
  • You can call m.getParameterTypes() to get all the actual types. It would return "List" for the example. – Lee Meador Mar 04 '16 at 01:40
  • If genericParameterTypes[i] is not an instanceof ParameterizedType, it could be a Class. That means that a particular argument to the method isn't parameterized at all. – Lee Meador Mar 04 '16 at 01:42
  • I suspect you have an instance of the sub class of the class in which the generic method resides, and various other constraints which mean that the types have been stored, you can find the names and types of the generic parameters on that super class (eg. these parameters are the T,U, and V from "public class myClass ...). Call getGenericSuperClass() on the Type for the instantiated class. (Dunno how to do it if that method is farther up the hierarchy of classes.) With names and types you can tell the Class of the method argument when the method is called in the instantiated class. – Lee Meador Mar 04 '16 at 01:55
20

There is a solution actually, by applying the "anonymous class" trick and the ideas from the Super Type Tokens:

public final class Voodoo {
    public static void chill(final List<?> aListWithSomeType) {
        // Here I'd like to get the Class-Object 'SpiderMan'
        System.out.println(aListWithSomeType.getClass().getGenericSuperclass());
        System.out.println(((ParameterizedType) aListWithSomeType
            .getClass()
            .getGenericSuperclass()).getActualTypeArguments()[0]);
    }
    public static void main(String... args) {
        chill(new ArrayList<SpiderMan>() {});
    }
}
class SpiderMan {
}

The trick lies in the creation of an anonymous class, new ArrayList<SpiderMan>() {}, in the place of the original (simple) new ArrayList<SpiderMan>(). The use of an anoymous class (if possible) ensures that the compiler retains information about the type argument SpiderMan given to the type parameter List<?>. Voilà !

  • 1
    This indirectly answers the question: The original problem has no solution. For the type to retains its generic parameters, it needs to be embedded in another type, such as a subclass. This example shows how you need to change the calling code to make it possible to recover the generic type parameter in chill(). – Florian F Nov 10 '17 at 16:20
  • FYI, first link is dead – Ashvin Sharma Mar 02 '20 at 07:05
  • 1
    @AshvinSharma I believe that the same material is available here: https://rgomes.info/using-typetokens-to-retrieve-generic-parameters/ – Yann-Gaël Guéhéneuc Mar 20 '20 at 17:32
9

Appendix to @DerMike's answer for getting the generic parameter of a parameterized interface (using #getGenericInterfaces() method inside a Java-8 default method to avoid duplication):

import java.lang.reflect.ParameterizedType; 

public class ParametrizedStuff {

@SuppressWarnings("unchecked")
interface Awesomable<T> {
    default Class<T> parameterizedType() {
        return (Class<T>) ((ParameterizedType)
        this.getClass().getGenericInterfaces()[0])
            .getActualTypeArguments()[0];
    }
}

static class Beer {};
static class EstrellaGalicia implements Awesomable<Beer> {};

public static void main(String[] args) {
    System.out.println("Type is: " + new EstrellaGalicia().parameterizedType());
    // --> Type is: ParameterizedStuff$Beer
}
Campa
  • 4,267
  • 3
  • 37
  • 42
  • This isn't what the original post was asking for. Here, you have create a subclass of `Awesomeable`. In that case the type information is preserved. If you pass `new Awesomable ()` to the method wt won't work. – Florian F Nov 10 '17 at 16:19
  • @FlorianF If you pass on an `Awesomable` on the fly without the explicit definition of a concrete subclass like `EstrellaGalicia` in this case, still it is getting out the parameterized type: I ran it right now: `System.out.println("Type is: " + new Awesomable() {}.parameterizedType());` ---> *Type is: ParameterizedStuff$Beer* – Campa Nov 13 '17 at 08:33
7

Nope, that is not possible. Due to downwards compatibility issues, Java's generics are based on type erasure, i.a. at runtime, all you have is a non-generic List object. There is some information about type parameters at runtime, but it resides in class definitions (i.e. you can ask "what generic type does this field's definition use?"), not in object instances.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
  • Though java could store this as meta data during runtime, couldn't it? I hoped it would. Bad Luck. – cimnine Dec 14 '09 at 14:27
  • 1
    @user99475: Nope. I am right and Andrey is wrong. He is referring to the type information I mention in the second part of my answer, but that is not what the question asks for. – Michael Borgwardt Aug 31 '11 at 12:05
6

Because of type erasure the only way to know the type of the list would be to pass in the type as a parameter to the method:

public class Main {

    public static void main(String[] args) {
        doStuff(new LinkedList<String>(), String.class);

    }

    public static <E> void doStuff(List<E> list, Class<E> clazz) {

    }

}
Jared Russell
  • 10,804
  • 6
  • 30
  • 31
6

As pointed out by @bertolami, it's not possible to us a variable type and get its future value (the content of typeOfList variable).

Nevertheless, you can pass the class as parameter on it like this:

public final class voodoo {
    public static void chill(List<T> aListWithTypeSpiderMan, Class<T> clazz) {
        // Here I'd like to get the Class-Object 'SpiderMan'
        Class typeOfTheList = clazz;
    }

    public static void main(String... args) {
        chill(new List<SpiderMan>(), Spiderman.class );
    }
}

That's more or less what Google does when you have to pass a class variable to the constructor of ActivityInstrumentationTestCase2.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Snicolas
  • 37,840
  • 15
  • 114
  • 173
3

No it isn't possible.

You can get a generic type of a field given a class is the only exception to that rule and even that's a bit of a hack.

See Knowing type of generic in Java for an example of that.

Community
  • 1
  • 1
cletus
  • 616,129
  • 168
  • 910
  • 942
3

Here is another trick. Use a generic vararg array

import java.util.ArrayList;

class TypedArrayList<E> extends ArrayList<E>
{
    @SafeVarargs
    public TypedArrayList (E... typeInfo)
    {
        // Get generic type at runtime ...
        System.out.println (typeInfo.getClass().getComponentType().getTypeName());
    }
}

public class GenericTest
{
    public static void main (String[] args)
    {
        // No need to supply the dummy argument
        ArrayList<Integer> ar1 = new TypedArrayList<> ();
        ArrayList<String> ar2 = new TypedArrayList<> ();
        ArrayList<?> ar3 = new TypedArrayList<> ();
    }
}
  • 1
    This is a great hack! Although it only works if the class is (implicitly) `final`. If you have a class that extends `TypedArrayList`, you get back `java.lang.Object`. – Sina Madani Jul 10 '23 at 14:53
3

I noticed that many people lean towards the getGenericSuperclass() solution:

class RootGeneric<T> {
  public Class<T> persistentClass = (Class<T>)
    ((ParameterizedType)getClass().getGenericSuperclass())
      .getActualTypeArguments()[0];
}

However, this solution is error prone. It will not work properly if there are generics in the descendants. Consider this:

class Foo<S> extends RootGeneric<Integer> {}

class Bar extends Foo<Double> {}

Which type will Bar.persistentClass have? Class<Integer>? Nope, it will be Class<Double>. This will happen due to getClass() always returns the top most class, which is Bar in this case, and its generic super class is Foo<Double>. Hence, the argument type will be Double.

If you need a reliable solution which doesn't fail I can suggest two.

  1. Use Guava. It has a class that was made exactly for this purpose: com.google.common.reflect.TypeToken. It handles all the corner cases just fine and offers some more nice functionality. The downside is an extra dependency. Given you've used this class, your code would look simple and clear, like this:
class RootGeneric<T> {
  @SuppressWarnings("unchecked")
  public final Class<T> persistentClass = (Class<T>) (new TypeToken<T>(getClass()) {}.getType());
}
  1. Use the custom method below. It implements a significantly simplified logic similar to the Guava class, mentioned above. However, I'd not guarantee it's error prone. It does solve the problem with the generic descendants though.
abstract class RootGeneric<T> {
  @SuppressWarnings("unchecked")
  private Class<T> getTypeOfT() {
    Class<T> type = null;
    Class<?> iter = getClass();
    while (iter.getSuperclass() != null) {
      Class<?> next = iter.getSuperclass();
      if (next != null && next.isAssignableFrom(RootGeneric.class)) {
        type =
            (Class<T>)
                ((ParameterizedType) iter.getGenericSuperclass()).getActualTypeArguments()[0];
        break;
      }
      iter = next;
    }
    if (type == null) {
      throw new ClassCastException("Cannot determine type of T");
    }
    return type;
  }
}
real4x
  • 1,433
  • 12
  • 11
  • I get the following error with the Guava solution: `java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class` – Aluthren Dec 21 '20 at 14:02
2

You can get the type of a generic parameter with reflection like in this example that I found here:

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class Home<E> {
    @SuppressWarnings ("unchecked")
    public Class<E> getTypeParameterClass(){
        Type type = getClass().getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) type;
        return (Class<E>) paramType.getActualTypeArguments()[0];
    }

    private static class StringHome extends Home<String>{}
    private static class StringBuilderHome extends Home<StringBuilder>{}
    private static class StringBufferHome extends Home<StringBuffer>{}   

    /**
     * This prints "String", "StringBuilder" and "StringBuffer"
     */
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        Object object0 = new StringHome().getTypeParameterClass().newInstance();
        Object object1 = new StringBuilderHome().getTypeParameterClass().newInstance();
        Object object2 = new StringBufferHome().getTypeParameterClass().newInstance();
        System.out.println(object0.getClass().getSimpleName());
        System.out.println(object1.getClass().getSimpleName());
        System.out.println(object2.getClass().getSimpleName());
    }
}
Community
  • 1
  • 1
madx
  • 6,723
  • 4
  • 55
  • 59
1

The quick answer the the Question is no you can't, because of Java generic type erasure.

The longer answer would be that if you have created your list like this:

new ArrayList<SpideMan>(){}

Then in this case the generic type is preserved in the generic superclass of the new anonymous class above.

Not that I recommend doing this with lists, but it is a listener implementation:

new Listener<Type>() { public void doSomething(Type t){...}}

And since extrapolating the generic types of super classes and super interfaces change between JVMs, the generic solution is not as straight forward as some answers might suggest.

Here is now I did it.

TacB0sS
  • 10,106
  • 12
  • 75
  • 118
0

This is impossible because generics in Java are only considered at compile time. Thus, the Java generics are just some kind of pre-processor. However you can get the actual class of the members of the list.

bertolami
  • 2,896
  • 2
  • 24
  • 41
0

You cannot get a generic parameter from a variable. But you can from a method or field declaration:

Method method = getClass().getDeclaredMethod("chill", List.class);
Type[] params = method.getGenericParameterTypes();
ParameterizedType firstParam = (ParameterizedType) params[0];
Type[] paramsOfFirstGeneric = firstParam.getActualTypeArguments();
Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
Anton Pogonets
  • 1,162
  • 7
  • 16
  • This gives me a Exception in thread "main" java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl – Lluis Martinez Jan 14 '14 at 14:49
  • 'params' are 'Type's which has several subinterfaces. TypeVariableImpl is one that is used for method arguments and it tells the name of the generic inside the <>s, not the Class it represents. – Lee Meador Mar 04 '16 at 02:05
0

Just for me reading this snippet of code was hard, I just divided it into 2 readable lines :

// assuming that the Generic Type parameter is of type "T"
ParameterizedType p = (ParameterizedType) getClass().getGenericSuperclass();
Class<T> c =(Class<T>)p.getActualTypeArguments()[0];

I wanted to create an instance of the Type parameter without having any parameters to my method :

publc T getNewTypeInstance(){
    ParameterizedType p = (ParameterizedType) getClass().getGenericSuperclass();
    Class<T> c =(Class<T>)p.getActualTypeArguments()[0];

    // for me i wanted to get the type to create an instance
    // from the no-args default constructor
    T t = null;
    try{
        t = c.newInstance();
    }catch(Exception e){
        // no default constructor available
    }
    return t;
}
Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
Ahmed Adel Ismail
  • 2,168
  • 16
  • 23
0

I've coded this for methods which expect to accept or return Iterable<?...>. Here is the code:

/**
 * Assuming the given method returns or takes an Iterable<T>, this determines the type T.
 * T may or may not extend WindupVertexFrame.
 */
private static Class typeOfIterable(Method method, boolean setter)
{
    Type type;
    if (setter) {
        Type[] types = method.getGenericParameterTypes();
        // The first parameter to the method expected to be Iterable<...> .
        if (types.length == 0)
            throw new IllegalArgumentException("Given method has 0 params: " + method);
        type = types[0];
    }
    else {
        type = method.getGenericReturnType();
    }

    // Now get the parametrized type of the generic.
    if (!(type instanceof ParameterizedType))
        throw new IllegalArgumentException("Given method's 1st param type is not parametrized generic: " + method);
    ParameterizedType pType = (ParameterizedType) type;
    final Type[] actualArgs = pType.getActualTypeArguments();
    if (actualArgs.length == 0)
        throw new IllegalArgumentException("Given method's 1st param type is not parametrized generic: " + method);

    Type t = actualArgs[0];
    if (t instanceof Class)
        return (Class<?>) t;

    if (t instanceof TypeVariable){
        TypeVariable tv =  (TypeVariable) actualArgs[0];
        AnnotatedType[] annotatedBounds = tv.getAnnotatedBounds();///
        GenericDeclaration genericDeclaration = tv.getGenericDeclaration();///
        return (Class) tv.getAnnotatedBounds()[0].getType();
    }

    throw new IllegalArgumentException("Unknown kind of type: " + t.getTypeName());
}
Ondra Žižka
  • 43,948
  • 41
  • 217
  • 277
-1

Use:

Class<?> typeOfTheList = aListWithTypeSpiderMan.toArray().getClass().getComponentType();
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • This is wrong. `toArray()` returns `Object[]`. You can't and shouldn't depend on it being some particular subtype. – Radiodef Aug 15 '18 at 22:48