Java 中的泛型会被类型擦除,那为什么在运行期依然能够应用反射获取到具体的泛型类型?
上面从知乎上看到一个答案,现摘录如下:
在运行期只能获取以后 class 文件中蕴含泛型信息的泛型类型,而不能在运行时动静获取某个泛型援用的类型 。 事实上所谓的泛型的类型擦除是指把某个具体的泛型援用在编译期实现类型查看后,擦除成了 Object 而失落了它运行时所赋予的类型信息。举例来说,真正没有擦除的泛型应该是能轻而易举地执行如下操作的:
public class Test<T> {public T[] getArray(int length) {
Class<T> clazz = T.class;
return (T[])Array.newInstance(clazz, length);
}
}
在以上的代码中,假如泛型的信息没有被擦除,您在任何地位 new 进去的 Test 实例都会保留本人的“T”类型信息,那么 getArray 办法就能够获取到理论 T 的 class 信息。而在类型擦除后,下面代码中是没有任何方法在 getArray 办法外部获取到 T 的类型信息的,这才是擦除后的实际效果。您所说的能够通过反射获取到的泛型信息肯定是某个 class 作为成员变量、办法返回值等地位的具体泛型类型,举例来说:
public class Test<T> {
private Test<Integer> test;
private T item;
}
在下面的代码中,您能够通过反射获取成员 test 的 Integer 泛型信息,然而无奈获取 item 的理论类型 。这部分我查看了 OpenJDK 8 的相干源码,从原理上讲, 例子中的 test 成员编译时会将 Integer 信息编译进 class 字节码,从而反射零碎就能够获取到这个信息,如下图所示:
您能够看到,实际上 test 的泛型信息是间接被编译进字节码了。
而这个办法实质上的操作是从曾经加载好的 class 信息中获取 fieldDescriptor,从而产生 Field 对象的 oop,把 class 的 field 信息注入进去,返回给 Java 端的调用方。而再追溯 class 中产生 fieldDescriptor 的代码能够发现,事实上这个信息就是在 JVM 加载字节码的时候,JVM 将解析到字节码的泛型信息保留下来的 。也就说 在类加载阶段,JVM 就将字节码中写死的泛型信息保留了下来。而您反射的时候,反射零碎天然就能够获取到该信息 ,从而您能够通过 getGenericType() 来获取到 Type 信息,从而解析出泛型类型。
而 例子中定义的 item 这类泛型援用来说,它们的泛型信息不来自于本身的 class,在编译实现通过类型查看后,类型零碎中它们就等同于 Object,这种泛型是无奈通过反射获取的,也就是说这类类型信息被擦除了。
作者:陆萌萌
链接:https://www.zhihu.com/questio…
起源:知乎
著作权归作者所有。商业转载请分割作者取得受权,非商业转载请注明出处。