目录
一. 泛型擦除是什么
java泛型是编译期的泛型,不是运行时的泛型
java语言是跨平台的,每个平台都有对应的JVM(java虚拟机),编写的java源码不能直接在JVM中运行,能在JVM中运行的是字节码,一般都以.class文件格式存在,java源码文件转换成.class字节码文件的过程称为编译期,编译期会对java源码严格校验,生成合格的字节码。编译过程中,编译器遇到泛型代码(泛型类、方法的定义和使用)会进行特殊处理,主要进行检查类型安全并擦除泛型信息,编译之后生成的.class字节码不包含泛型信息(实际上会保留一些泛型信息,只有在某些特殊情况下才会使用),JVM虚拟机运行时不知道泛型的存在,也不会针对泛型做特殊处理。如下代码:
/**
这是一个很简单的泛型化类,拥有一个属性var,和该属性的setter和getter方法
**/
public class Generic<T> {
private T var;
public T getVar() {
return var;
}
public void setVar(T var) {
this.var = var;
}
public static void main(String[] args) {
// 设置类型为Integer
Generic<Integer> inGeneric = new Generic<Integer>();
// 设置属性值
inGeneric.setVar(10);
// 输出值
System.out.println(inGeneric.getVar().intValue());
}
}
上面定义了泛型类Generic, 其中属性var的声明类型也是泛型修饰,并提供了var的getter和setter方法。在main方法里首先创建了Generic实例,传入类型为Integer,意味着属性var的类型是Integer,即: Generic<Integer> inGeneric = new Generic<Integer>(),并调用inGeneric.setVar(10)设置var值为10,调用inGeneric.getVar().intValue()获取var的值。上面这段示例代码虽然简单,但是在类定义、属性声明、方法入参与出参、泛型类实例创建和方法调用上都使用了,通过观察编译后的.class文件,比较能全面直观地了解泛型擦除的效果。
使用javap -v 命令输出.class字节码内容
javap -v Generic.class
输出结果,由于字节码内容比较多,这里只摘出部分内容:
Classfile /D:/workspace/learn-class/bin/cn/learn/classes/Generic.class
Last modified 2019-4-8; size 1247 bytes
MD5 checksum 917868e0a5c70d19355976a97fe8b62e
Compiled from "Generic.java"
public class cn.learn.classes.Generic<T extends java.lang.Object> extends java.lang.Object
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Class #2 // cn/learn/classes/Generic
#2 = Utf8 cn/learn/classes/Generic
#3 = Class #4 // java/lang/Object
#4 = Utf8 java/lang/Object
#5 = Utf8 var
....
public T getVar();
descriptor: ()Ljava/lang/Object;
flags: ACC_PUBLIC
S