在运行期获取java泛型类型的方法
问题的来由
Google的Guice是一个轻量级的DI框架,在进行依赖关系的解析时,能自动将一个模型类T与它的模型提供者Provider<T>
进行关联,也就是Guice能够解析Provider<T>
中的泛型类型T。学习了一下源码,为了在运行时获取泛型的类型,Guice提供了TypeLiteral<T>
类,继承了TypeLiteral<T>
的类可以获取到其泛型类型。
学习的过程
而这与我对泛型的理解是不一样的,以前学到的是泛型信息在编译期会擦除,在运行时是无法获取到泛型类型的,这主要是为了实现向后兼容,不管一个类型Class<T>
,不管它的泛型类型是什么,在运行时它都是同一个类型,比如说下面的表达式是成立的。
assert new ArrayList<String>().getClass() == new ArrayList<Integer>().getClass();
而实际上在一种情况下,java提供了在运行期获取泛型类型的方法,如下面的测试代码:
public class GenericTest {
class MyGenericClass<T> { }
class MyStringSubClass extends MyGenericClass<String> { }
public static void main(String[] args) {
System.out.print(((ParameterizedType) MyStringSubClass.class
.getGenericSuperclass()).getActualTypeArguments()[0]);
}
}
上面的测试代码执行后返回“class java.lang.String”,实现了获取泛型类型。MyGenericClass类有一个泛型T,MyStringSubClass继承了MyGenericClass并赋值了T=String。在这种情况下, java编译器能够在MyStringSubClass的字节码中记录下它继承的MyGenericClass类的泛型类型String。之所以这种情况下编译器能够记录下泛型信息,是因为MyStringSubClass能够确定T=String,不需要擦除泛型信息来实现向后兼容。
所以回到Guice的例子中,Guice通过TypeLiteral<T>
类,继承TypeLiteral<T>
的子类都可以通过这种方法获取到继承基类TypeLiteral时指定的泛型类型。
Java API提供了Class.getGenericSuperclass方法返回Type表示其直接超类,如果这个超类是一个泛型类,那么返回类型实际是ParameterizedType,其getActualTypeArguments方法以数组的形式存储了其多个泛型参数。
gentyref库
虽然可以用上面的代码自己实现,但google提供了一个gentyref库帮助我们实现了获取泛型类型的功能,下面是一段测试代码:
public class GentyrefTest {
public <T> void chill(List<T> aListWithTypeT) {
System.out.println(GenericTypeReflector
.getTypeParameter(
aListWithTypeT.getClass(),Collection.class.getTypeParameters()[0]));
}
public void chillWildcard(List<?> aListWithTypeWildcard) {
System.out.println(
GenericTypeReflector.getTypeParameter(
aListWithTypeWildcard.getClass(),Collection.class.getTypeParameters()[0]));
}
public static void main(String... args) {
GentyrefTest test = new GentyrefTest();
test.chill(new ArrayList<String>(){});
test.chillWildcard(new ArrayList<Integer>(){});
}
}
这段代码的返回值是class java.lang.String class java.lang.Integer
其中需要注意的是,其原理和刚才分析的是一样的,所以不能直接使用new ArrayList<String>()
作为函数的参数,而是要像例子中一样,创建其子类,如new ArrayList<String>(){}
.
总结
通过这个问题学习到了一种在运行期能够获取泛型类型的方法,也加深了对泛型的理解。
参考
http://stackoverflow.com/questions/937933/where-are-generic-types-stored-in-java-class-files https://www.javacodegeeks.com/2013/12/advanced-java-generics-retreiving-generic-type-arguments.html http://rednaxelafx.iteye.com/blog/586212 http://www.oschina.net/question/946569_233598?fromerr=suQBoxXj