背景
我们都知道泛型本质上是提供类型的 "类型参数", 它们也被称为参数化类型 (parameterized type) 或参量多态(parametric polymorphism). 其实泛型思想并不是 Java 最先引入的, C++ 中的模板就是一个运用泛型的例子.
GJ(Generic Java)是对 Java 语言的一种扩展, 是一种带有参数化类型的 Java 语言. 用 GJ 编写的程序看起来和普通的 Java 程序基本相同, 只不过多了一些参数化的类型同时少了一些类型转换. 实际上, 这些 GJ 程序也是首先被转化成一般的不带泛型的 Java 程序后再进行处理的, 编译器自动完成了从 Generic Java 到普通 Java 的翻译.
什么是真实的 java 泛型
我们都知道编译器会进行泛型擦除, 编译器可以在对源程序 (带有泛型的 Java 代码) 进行编译时使用泛型类型信息保证类型安全, 对大量如果没有泛型就不会去验证的类型安全约束进行验证, 同时在生成的字节码当中, 将这些类型信息清除掉. 下面我们先验证一下:
- public static void main(String[] args) {
- ArrayList<Integer> ints = new ArrayList<Integer>();
- ints.add(1);
- ints.add(2);
- ints.add(3);
- ArrayList<String> sts = new ArrayList<String>();
- sts.add("a");
- sts.add("b");
- sts.add("c");
- System.out.println(ints.getClass() == sts.getClass());
- }
上面打印的结果是 true, 原因是:
按照理解, 泛型擦除后将不能找回原来的类型, 都是 Object 形式的, 真的如此吗?
看一下如下代码:
- import java.lang.reflect.ParameterizedType;
- import java.util.ArrayList;
- import java.util.List;
- 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> {}
- }
打印出
- class java.lang.String
- class com.javapuzzle.davidwang456.ClassTest$Bar
- class java.lang.String
- interface java.lang.CharSequence
你会发现每一个类型参数都被保留了, 而且在运行期可以通过反射机制获取到. 那么到底什么是 "类型擦除"? 至少某些东西被擦除了吧? 是的. 事实上, 除了结构化信息外的所有东西都被擦除了 -- 这里结构化信息是指与类结构相关的信息, 而不是与程序执行流程有关的. 换言之, 与类及其字段和方法的类型参数相关的元数据都会被保留下来, 可以通过反射获取到.
参考资料
[1] http://techblog.bozho.net/on-java-generics-and-erasure/
[2] https://www.ibm.com/developerworks/cn/java/j-lo-gj/?mhsrc=ibmsearch_a&mhq=类型擦除
来源: https://www.cnblogs.com/davidwang456/p/11880251.html