如何获取通用类型参数?

时间:2013-08-20 14:23:32

标签: java generics reflection generic-programming type-parameter

简单地:

public static class MyClass<T> {
    // i don't want to keep an instance of T, if it is not necessary.
    // and it is not nice, not neat.

    // Or, let's say, the member are in the form of :
    ArrayList<T> mArrayList = new ArrayList<T>();
    // the problem of getting the generic type parameter is still present.
}

@Test
public final void test() {
    MyClass<Integer> myObject = new MyClass<Integer>();
    getParamType( myObject );
}

private static final <T> void getParamType(final MyClass<T> _myObject) {
    System.out.println(_myObject.getClass().getTypeParameters()[0]);    // T
    System.out.println(((T) new Object()).getClass());                  // class java.lang.Object
}

如何让代码打印class java.lang.Integer

我知道很多stackoverflow线程正在询问(和回答)这个问题。但他们无法解决这个问题。

  • 我不知道为什么有些人需要调用getGenericSuperclass() - 因为在这个简单的案例中没有涉及继承。
  • 我也不能把它投到ParameterizedType

System.out.println((ParameterizedType) _myObject.getClass());
// Compile Error: Cannot cast from Class<capture#11-of ? extends TreeTest2.MyClass> to ParameterizedType

System.out.println((ParameterizedType) _myObject.getClass().getGenericSuperclass());
// Runtime Exception: java.lang.ClassCastException

根据@ Thomas的指南,我找到了 解决方法 获取class java.lang.Integer的方法。

首先,我们在测试代码中创建匿名(它需要是匿名的)MyClass<T>子类。 (这很奇怪。为什么它只支持子类?)

@Test
public final void test() {
    MyClass<Integer> myObject = new MyClass<Integer>() {};  // Anonymous sub-class
    getParamType( myObject );
}

然后我们可以使用getGenericSuperclass()方法获取Type,然后将其投放到ParameterizedType,然后使用getActualTypeArguments()

private static final <T> void getParamType(final MyClass<T> _myObject) {
    System.out.println( ((ParameterizedType) _myObject.getClass().getGenericSuperclass()).getActualTypeArguments()[0] );
}

完美打印class java.lang.Integer

这是不那么好因为测试代码应该模拟实际情况,用户很可能不会继续创建无意义的子类。

这种方法基于the TypeReference class的想法。但我真的不知道如何使用它。我试过了class MyClass<T> extends TypeReference<T>。但我仍然需要创建MyClass<T>的子类,以TypeReference.getType()打印class java.lang.Integer

请提供帮助,并感谢任何意见,因为最好的方法还没有。


基于上述解决方法的另一个问题:为什么只有匿名子类可以工作?

public static class SubMyClass<T> extends MyClass<T>{}

@Test
public final void test() {
    MyClass<Integer> myObject = new MyClass<Integer>() {};  // Anonymous sub-class
    getParamType( myObject );               // class java.lang.Integer

    MyClass<Integer> mySubObject = new SubMyClass<Integer>();   // named sub-class
    getParamType( mySubObject );            // T
}

MyClassgetParamType()未更改。)

4 个答案:

答案 0 :(得分:3)

这有点困难,因为Java故意不能这样做(“类型擦除”)。

变通方法称为super type tokens。关于这一点也有一些线索(如this onethat one)。

答案 1 :(得分:2)

当你有这样的问题时,你应该问问自己,如果没有泛型,你会怎么做?因为任何泛型程序都可以转换为没有泛型的等效程序。 (这种转换称为类型擦除。)因此,如果没有Generics就无法做到,那么你也无法使用Generics。

没有Generics的程序如下所示:

public static class MyClass {
    ArrayList mArrayList = new ArrayList();
}

@Test
public final void test() {
    MyClass myObject = new MyClass();
    Integer result = getParamType( myObject ); // how would you implement getParamType()?
}

答案 2 :(得分:1)

Java有一个名为Type Erasure的误导功能,专门阻止您这样做。

运行时不存在通用参数信息。

相反,您可以手动接受Class<T>

答案 3 :(得分:0)

要了解T的价值,您需要通过继承MyClass在类型定义中捕获它:

class MyStringClass extends MyClass<String> {}

如果需要,您也可以使用匿名类执行此操作:

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

要解析T的值,您可以使用TypeTools

Class<?> stringType = TypeResolver.resolveRawArgument(MyClass.class, myStringClass.getClass());
assert stringType == String.class;