How to get the class of type variable in Java Generics

54,268

Solution 1

The only way is to store the class in an instance variable and require it as an argument of the constructor:

public class ContainerTest<T>
{
    private Class<T> tClass;
    public ContainerTest(Class<T> tClass) {
        this.tCLass = tClass;
    }

    public void doSomething()
    {
        //access tClass here
    }
}

Solution 2

If you are interested in the reflection way, I found a partial solution in this great article: http://www.artima.com/weblogs/viewpost.jsp?thread=208860

In short, you can use java.lang.Class.getGenericSuperclass() and java.lang.reflect.ParameterizedType.getActualTypeArguments() methods, but you have to subclass some parent super class.

Following snippet works for a class that directly extends the superclass AbstractUserType. See the referenced article for more general solution.

import java.lang.reflect.ParameterizedType;


public class AbstractUserType<T> {

    public Class<T> returnedClass() {
        ParameterizedType parameterizedType = (ParameterizedType) getClass()
                .getGenericSuperclass();

        @SuppressWarnings("unchecked")
        Class<T> ret = (Class<T>) parameterizedType.getActualTypeArguments()[0];

        return ret;
    }

    public static void main(String[] args) {
        AbstractUserType<String> myVar = new AbstractUserType<String>() {};

        System.err.println(myVar.returnedClass());
    }

}

Solution 3

There is no "clean" way to get the Generic Type argument from within the class. Instead, a common pattern is to pass the Class of the Generic Type to the constructor and keep it as an inner property juste as done in the java.util.EnumMap implementation.

http://docs.oracle.com/javase/1.5.0/docs/api/java/util/EnumMap.html http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/EnumMap.java

public class ContainerTest<T> {

    Class<T> type;
    T t;

    public ContainerTest(Class<T> type) {
        this.type = type;
    }

    public void setT(T t) {
        this.t = t;
    }

    public T getT() {
        return t;
    }

    public void doSomething() {
        //There you can use "type" property.
    }
}

Solution 4

No. It is not possible because of type erasure (the type parameters are compiled as Object + type casts). If you really need to know/enforce the type in runtime you may store a reference to a Class object.

public class ContainerTest<T> {
   private final Class<T> klass;
   private final List<T> list = new ArrayList<T>();

   ContainerTest(Class<T> klass) {
     this.klass = klass;
   }

   Class<T> getElementClass() {
     return klass;
   }

   void add(T t) {
      //klass.cast forces a runtime cast operation
      list.add(klass.cast(t));
   }
}

Use:

ContainerTest<String> c = new ContainerTest<>(String.class);

Solution 5

There is a way to get the runtime type of the type parameter by using Guava's TypeToken to capture it. The solution's disadvantage is that you have to create an anonymous subclass each time you need an instance of Container.

class Container<T> {

    TypeToken<T> tokenOfContainedType = new TypeToken<T>(getClass()) {};

    public Type getContainedType() {
        return tokenOfContainedType.getType();
    }
}

class TestCase {

    // note that containerTest is not a simple instance of Container,
    // an anonymous subclass is created
    private Container<String> containerTest = new Container<String>() {};

    @Test
    public void test() {
        Assert.assertEquals(String.class, containerTest.getContainedType());
    }
}

The key of this solution is described in tha JavaDoc of TypeToken's constructor used in the code above:

Clients create an empty anonymous subclass. Doing so embeds the type parameter in the anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure.

Share:
54,268

Related videos on Youtube

ZeDonDino
Author by

ZeDonDino

Updated on October 26, 2020

Comments

  • ZeDonDino
    ZeDonDino over 3 years

    I've seen similar questions but they didnt help very much.

    For instance I've got this Generic Class:

    public class ContainerTest<T>
    {
    
        public void doSomething()
        {
            //I want here to determinate the Class of the type argument (In this case String)
        }
    }
    

    and Another Class which uses this Container Class

    public class TestCase
    {
    
        private ContainerTest<String> containerTest;
    
        public void someMethod()
        {
            containerTest.doSomething();
        }
    }
    

    Is it possible to determinate the Class of the type argument in method doSomething() without having an explicit type variable/field or any constructor in ContainerTest Class?

    Update: Changed format of ContainerTest Class

    • vikingsteve
      vikingsteve about 11 years
      if (t instanceof String) ?
    • ZeDonDino
      ZeDonDino about 11 years
      I've updated the question
    • Rohit Jain
      Rohit Jain about 11 years
      Can't you pass the class type as an argument only?
    • Elgirhath
      Elgirhath about 4 years
      I might be late, but there is a great solution. Use C# (:
  • hertzsprung
    hertzsprung about 11 years
    This is known as a 'type token'
  • ZeDonDino
    ZeDonDino about 11 years
    I would like to invoke this method without any arguments.
  • ZeDonDino
    ZeDonDino about 11 years
    Use this with caution, this is only supported In Java SE 7 and higher.
  • Javier
    Javier about 11 years
    ContainerTest<String> c = new ContainerTest<String>(String.class) //and so the diamond was born
  • Bob Wang
    Bob Wang about 11 years
    This is exactly what I would do.
  • ZeDonDino
    ZeDonDino about 11 years
    Well, I knew this. I hoped there is a way to get the Class out of the <String> and not passing the value into a constructor or field or similar
  • Renaud
    Renaud about 11 years
    @ZeDonDino I link EnumMap to show that even the OpenJDK is using this pattern in a standrad implementation.
  • zagyi
    zagyi about 11 years
    Wow, a downvote, that's nice... Could you at least drop a comment on why you don't like it?
  • Didier L
    Didier L about 11 years
    This is an interesting solution but I think it would be better suited for reflexion like guava does than for this question's use case. The problem with this solution is that it requires to create an anonymous class in each location where you instantiate it. (notice I am not the downvoter)
  • vach
    vach about 10 years
    Are you sure this is the only way?
  • Didier L
    Didier L about 10 years
    Except if you force the caller to subclass your class, yes. But it is usually not a problem…