Java 中协变、类型擦除与桥接方法
协变
① 为什么数组是协变的?
jdk1.5 之前不支持泛型,,想要为每个数组类型添加通用的 equals方法,所以设计成是协变的。
② 为什么数组需要在运行时保留类型信息?
由于数组是协变的,利用其多态特性,即 Integer[]
的实例对象可以赋给 Number[]
类型的变量。则可以向该 Integer[ ] 实例放入Double类型的元素。因此保留类型信息则可在运行时检查出类型错误,从而阻止此类问题。
③ java 中泛型不支持协变
由于类型擦除的关系(下例),泛型不支持协变。
④ 无法创建泛型数组
List[] li1 = new ArrayList[2]; // 可以 List<?>[] li2 = new ArrayList<?>[2]; // 可以 List<Integer>[] li3 = new ArrayList<Integer>[2]; // 编译报错
④ scala 中泛型支持协变
scala 中的协变仅用于不可变对象,比如 List 是支持协变的,而 ListBuffer 不支持协变。
类型擦除
问题
class Node<T> { public T data; public Node(T data) { this.data = data; } public void setData(T data) { System.out.println("Node.setData"); this.data = data; } } class MyNode extends Node<Integer> { public MyNode(Integer data) { super(data); } public void setData(Integer data) { System.out.println("MyNode.setData"); super.setData(data); } } class Main { public static void main(String[] args) { MyNode mn = new MyNode(5); Node n = mn; // A raw type - compiler throws an unchecked warning n.setData("Hello"); Integer x=mn.data; } }
桥接方法
编译后,由于类型擦除生成的一个辅助方法。
public class Node<T> { public T data; public Node(T data) { this.data = data; } public void setData(T data) { System.out.println("Node.setData"); this.data = data; } } public class MyNode extends Node<Integer> { public MyNode(Integer data) { super(data); } public void setData(Integer data) { System.out.println("MyNode.setData"); super.setData(data); } }
编译后
public class Node { public Object data; public Node(Object data) { this.data = data; } public void setData(Object data) { System.out.println("Node.setData"); this.data = data; } } class MyNode extends Node { // Bridge method generated by the compiler // public void setData(Object data) { setData((Integer) data); } public void setData(Integer data) { System.out.println("MyNode.setData"); super.setData(data); } // ... }
为了满足对象继承关系,生成立一个桥接方法。该桥接方法具有和 Node 类方法签名一致的方法,然后委托具体的类型方法。
注:类型擦除发生在编译时,指的是定义泛型模板的类,而不是注入泛型参数的类(或者说是实现类),其模板类中跟泛型有关的参数都会被擦除成上界类型(或Object),实现类中泛型参数的类型会保留,此外如果通过继承的方式注入泛型参数,还会生成相应桥接方法,以满足继承关系。
233
参考资料
https://hongjiang.info/scala-type-system-array-type/#:~:text=scala%E9%87%8C%E4%B8%8D%E6%94%AF%E6%8C%81%E6%95%B0%E7%BB%84,%5BAny%5D%20%E7%B1%BB%E5%9E%8B%E7%9A%84%E5%8F%98%E9%87%8F%E3%80%82
https://zq99299.github.io/java-tutorial/java/generics/bridgeMethods.html#%E6%A1%A5%E6%8E%A5%E6%96%B9%E6%B3%95