How to get the class of type variable in Java Generics

前端 未结 7 1420
北恋
北恋 2021-02-01 17:24

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

For instance I\'ve got this Generic Class:

public class ContainerTest
{

    public          


        
相关标签:
7条回答
  • 2021-02-01 18:06

    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.

    0 讨论(0)
  • 2021-02-01 18:07

    If You can define like this

    public class ContainerTest<T>
    {
    
        public void doSomething(T clazz)
        {
    
        }
    }
    

    Then it is possible

    0 讨论(0)
  • 2021-02-01 18:11

    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());
        }
    
    }
    
    0 讨论(0)
  • 2021-02-01 18:12

    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
        }
    }
    
    0 讨论(0)
  • 2021-02-01 18:14

    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);
    
    0 讨论(0)
  • 2021-02-01 18:27

    To learn the value of T you'll need to capture it in a type definition by subclassing ContainerTest:

    class MyStringClass extends ContainerTest<String> {}
    

    You can also do this with an anonymous class if you want:

    ContainerTest<String> myStringClass = new ContainerTest<String>{}();
    

    Then to resolve the value of T, you can use TypeTools:

    Class<?> stringType = TypeResolver.resolveRawArgument(ContainerTest.class, myStringClass.getClass());
    assert stringType == String.class;
    
    0 讨论(0)
提交回复
热议问题