实际上,从虚拟机的角度看,不存在泛型概念。
泛型是运用在编译时期的技术:编译时编译器会按照<类型名>的类型对容器中的元素进行检查,检查不匹配,就编译失败。如果全部检查成功,则编译通过,但编译通过后产生的 .class文件中还有<类型名>这个标识,即字节码的类文件中有泛型,但是运行时并没有泛型这就是泛型的擦除。
可以通过使用javap反编译查看字节码文件,可以看到其中包含泛型
一句话总结就是:在.java文件运用泛型技术时,编译器在文件编译通过后运行时自动擦除泛型标识。
如果需要使用泛型的类型,则需要通过反射机制进行保存
abstract class A1<TF> {
private Class<TF> clz; // 就是T的具体类型
// 在构造器中获取T的具体类型
public A1() {
ParameterizedType c = (ParameterizedType) this.getClass().getGenericSuperclass(); // 获取到父类型的定义com.yan6.A1<T>
clz=(Class)(c.getActualTypeArguments()[0]); //获取真实的类型参数 T}
}
由于泛型的擦除,运行时并没有泛型机制,同时也没有使用向下类型转换,那么为何运行时无异常?
这是由于泛型的补偿
List<String> list=new ArrayList<>();
for(String tmp:list) System.out.println(tmp.length()); //泛型的补偿
编译器在擦除泛型后,会自动将类型转换为原定义的泛型,这样就不必再做向下类型转换了。
for(Object tmp:list){
if(tmp instanceof String){
System.out.println(((String)tmp).length());
}
}
泛型的擦除和补偿这两个机制都是编译器内部自动完成的。
可以通过反射获取类型参数
泛型的局限性
- 不能使用基本类型
- 不能使用泛型类异常
- 不能使用泛型数组不能实例化参数类型对象。例如T ob = new T();
import java.lang.reflect.ParameterizedType;
public class Test1 {
public static void main(String[] args) throws Exception {
B1 bb=new B1();
System.out.println("dd:"+bb.getTt());
}
}
abstract class A1<TF>{
private Class<TF> clz; //就是T的具体类型
private TF tt;
//在构造器中获取T的具体类型
public A1() throws Exception {
ParameterizedType c=(ParameterizedType)this.getClass().getGenericSuperclass(); //获取到父类型的定义 com.yan6.A1<T>
clz=(Class)(c.getActualTypeArguments()[0]); //获取真实的类型参数 T
tt=clz.newInstance();
System.out.println(c.getActualTypeArguments()[0]); //class java.lang.String
// Class c=this.getClass(); //class com.yan6.B1
// System.out.println(c);
}
public Class getParent() {
return this.clz;
}
public TF getTt() {
return tt;
}
public void setTt(TF tt) {
this.tt = tt;
}
}
class B1 extends A1<String>{
public B1() throws Exception {
super();
}
}
import java.lang.reflect.Method;
public class Test2 {
public static void main(String[] args) throws Exception {
String clz="java.util.Date";
System.out.println(Class.forName(clz).newInstance());
A2 aa=new A2();
// aa.pp();
Method method=aa.getClass().getDeclaredMethod("pp");
method.setAccessible(true);
Object res=method.invoke(aa);
System.out.println(res);
}
}
class A2{
private String pp() {
return "neimengren";
}
}