“编译期间擦除泛型”是常识(好吧,类型参数和实参实际上就是被擦除的)。 这是由于“类型擦除”而发生的。 但这是错误的,正如许多开发人员所假设的那样,删除了<..>
符号内指定的所有内容。 请参见下面的代码:
public class ClassTest {public static void main(String[] args) throws Exception {ParameterizedType type = (ParameterizedType) Bar.class.getGenericSuperclass();System.out.println(type.getActualTypeArguments()[0]);ParameterizedType fieldType = (ParameterizedType) Foo.class.getField("children").getGenericType();System.out.println(fieldType.getActualTypeArguments()[0]);ParameterizedType paramType = (ParameterizedType) Foo.class.getMethod("foo", List.class).getGenericParameterTypes()[0];System.out.println(paramType.getActualTypeArguments()[0]);System.out.println(Foo.class.getTypeParameters()[0].getBounds()[0]);}class Foo<E extends CharSequence> {public List<Bar> children = new ArrayList<Bar>();public List<StringBuilder> foo(List<String> foo) {return null; }public void bar(List<? extends String> param) {}}class Bar extends Foo<String> {}
}
你知道那是什么吗?
类java.lang.String
类ClassTest $ Bar
类java.lang.String
类java.lang.StringBuilder
接口java.lang.CharSequence
您会看到每个类型的参数都会保留下来,并且可以在运行时通过反射进行访问。 但是,什么是“类型擦除”? 必须删除某些内容吗? 是。 实际上,除结构化结构外,其他所有结构均与之相关–上面的所有内容都与类的结构有关,而不是与程序流程有关。 换句话说,有关类的类型参数及其字段和方法的元数据被保留以通过反射进行访问。
但是,其余部分将被删除。 例如,以下代码:
List<String> list = new ArrayList<>();
Iterator<String> it = list.iterator();
while (it.hasNext()) {String s = it.next();
}
实际上将被转换为此(两个片段的字节码相同):
List list = new ArrayList();
Iterator it = list.iterator();
while (it.hasNext()) {String s = (String) it.next();
}
因此,将删除您在方法主体中定义的所有类型参数,并在需要的地方添加强制类型转换。 另外,如果定义了一个方法以接受List<T>
,则此T
将被转换为Object(或如果声明了其边界,则转换为其界限。这就是为什么您不能执行new T()
(顺便说一句,有关此擦除的公开问题 )。
到目前为止,我们已经涵盖了类型擦除定义的前两点。 第三个是关于桥接方法。 我已经用这个stackoverflow问题(和答案)进行了说明 。
所有这两个“道德”。 首先,java泛型很复杂。 但是您可以在不了解所有复杂性的情况下使用它们。
其次,不要假设所有类型信息都被删除了–结构类型参数在那里,因此,如果需要,可以使用它们(但不要过分依赖反射)。
翻译自: https://www.javacodegeeks.com/2014/11/on-java-generics-and-erasure.html