虚拟机没有泛型类型的对象–所有对象都属于普通类。
那么虚拟机怎么工作?
类型擦除、原始类型、给JVM的指令、桥方法、Java泛型转换的事实
Java泛型转换的事实:
-
虚拟机中没有泛型,只有普通的类和方法
-
所有的类型参数都用他们的限定类型替换
-
桥方法被合成来保持多态
-
为保持类型安全性,必要时插入强制类型转换
类型擦除–type erasure
Java泛型的处理在编译器中运行,编译生成的字节码bytecode不包含泛型信息,泛型信息在编译处理时被擦除(erasure),这个过程即类型擦除
原始类型(raw type)
定义一个泛型类型,编译器自动提供一个相应的原始类型(raw type),原始类型就是删除类型参数(包括泛型类型变量及其限定类型)后的泛型类型名。擦除类型变量,原始类型替换为限定类型(无限定则用Object)
泛型变量有限定类型示例: <T extends Comparable & Serializable> ,因为Comparable包含实现方法CompareTo,而Serializable只是个标签(Tagging)接口,若二者对调位置,则原始类型将用Serializable来替换,这样在编译器必要时要向Comparable插入强制类型转换,所以为了提高效率,最好将标签(Tagging)接口放在边界列表的末尾
标签(Tagging)接口:没有方法的接口
给JVM的指令:
-
擦除方法返回类型,编译器将方法调用处理为两条JVM虚拟机指令
-
对原始方法的调用(返回Object类型或限定类型)
-
将返回的Object类型强制转换为泛型类型的具体类型(如Employee)
-
补充#当存取一个泛型域时也要插入强制类型转换
如何证明被擦除了?
package com.wht.generic;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<String>();
list1.add("abc");
ArrayList<Integer> list2 = new ArrayList<Integer>();
list2.add(123);
System.out.println(list1.getClass() == list2.getClass());
}
}
返回true
只不过在编译的时候,add不匹配的类型数据直接报错。
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1); //这样调用 add 方法只能存储整形,因为泛型类型的实例为 Integer
list.getClass().getMethod("add", Object.class).invoke(list, "asd");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
这个例子就说明了另外一个问题,java的泛型是假泛型,只适用于编译器的校验,运行时已经无效。
桥方法目前不做整理,后面再看