文章目录
java泛型原理(泛型擦除)
Java的泛型是伪泛型,因为在编译期间,所有的泛型信息都会被擦除掉,我们常称为泛型擦除.
java的泛型是由编译期这个层次实现的,字节码是不包含泛型中的类型信息的。
可以证明发生类型擦除的例子
public static void main(String[] args) throws IOException, ClassNotFoundException {
List<String> integerList = new ArrayList<>();
List<Integer> floatList = new ArrayList<>();
System.out.println(integerList.getClass() == floatList.getClass());
}
//通过反射可以绕过泛型类型限定
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
List<Integer> integerList = new ArrayList<>();
integerList.add(new Integer(11));
Class<? extends List> aClass = integerList.getClass();
Method add = aClass.getMethod("add", Object.class);
add.invoke(integerList,"1111");
for (Object e:integerList){
System.out.println(e);
}
}
类型擦除之后保存的是原始类型
原始类型就是擦除了泛型信息,最后在字节码中的类型变量的真正类型。无论何时定义一个泛型类型,相应的原始类型都会被自动地提供。类型变量被擦除,并使用其限定类型(无限定的变量用Object替换)。
如果泛型的定义是 擦除之后就是Object。
如果泛型定义是<T extends Collection & List>哪个在前就是哪个。现在是Collection
泛型方法的调用
在调用泛型方法时可以不指定泛型。在不指定泛型的情况下,泛型变量的类型为该方法中的几种类型的同一个父类的最小级,直到Object。在指定泛型的时候,该方法中的几种类型必须是该泛型实例类型或者其子类。
class Test {
public static void main(String[] args) {
//不指定泛型的时候
int a1 = add(1, 2); /
Number b1 = add(1, 1.2);
Object c1 = add(1, "my");
//指定泛型的时候
int a = Test.<Integer>add(1, 2);//指定了Integer,所以只能为Integer类型或者其子类
// int b = Test.<Integer>add(1, 2.2);//编译错误,指定了Integer,不能为Float
Number c = Test.<Number>add(1, 2.2); //指定为Number,所以可以为Integer和Float
}
public static <T> T add(T x, T y) {
return x;
}
}
泛型的类型检测针对谁?
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<String>();
arrayList.add(1); //编译报错
ArrayList<String> arrayList1 = new ArrayList(); //第一种 情况
arrayList1.add(1); //编译报错
ArrayList arrayList2 = new ArrayList<String>();//第二种 情况
arrayList2.add(1);
}
类型检查就是针对引用,谁是一个引用,用这个引用调用泛型方法,就会对这个引用调用方法进行类型检查,而无关它真正引用的对象。
类型转换
当一个具有擦除返回类型的泛型方法被调用时会进行强制类型转换。
在调用这个返回一个泛型的地方会根据类型变量进行强转(做了一个checkcast()操作,即检查< String >中的类型并强转)
类型擦除和多态的冲突解决办法
JVM采用桥方法来完成这项功能。
父类在进行类型擦除之后会变成原始数据类型。编译器会自动生成桥方法,桥方法的参数类型都是Object,桥方法的内部实现就是调用子类实现的方法。