• java 泛型的类型擦除和桥方法


    oracle原文地址:https://docs.oracle.com/javase/tutorial/java/generics/erasure.html 

    在Java中,泛型的引入是为了在编译时提供强类型检查和支持泛型编程。为了实现泛型,Java编译器应用类型擦除实现:

           1、  用类型参数(type parameters)的限定(如果没有就用Object)替换泛型类型中的所有类型参数。

           2、  需要保持类型安全的时候插入类型转换(隐含插入)

           3、  在extened 泛型类型中生成桥方法来保证多态性

       类型擦除确保不会为已参数化了的类型(paramterized types)产生新类,这样泛型能保证没有运行时的负载。

    泛型类型擦除

          在类型擦除过程中,java编译器擦除所有类型参数,用它的限定或者Object(没限定时)替换。

         考虑下面的泛型类:  

     1 public class Node<T> {
     2  
     3     private T data;
     4     private Node<T> next;
     5  
     6     public Node(T data, Node<T> next) }
     7         this.data = data;
     8         this.next = next;
     9     }
    10  
    11     public T getData() { return data; }
    12     // ...
    13 }

    因为类型参数T是非限定的,Java编译器使用Object替换它:  

     1  public class Node {
     2  
     3     private Object data;
     4     private Node next;
     5  
     6     public Node(Object data, Node next) {
     7         this.data = data;
     8         this.next = next;
     9     }
    10  
    11     public Object getData() { return data; }
    12     // ...
    13  }

    下面的例子,泛型Node类使用了限定类型参数:

     1  public class Node<T extends Comparable<T>> {
     2  
     3     private T data;
     4     private Node<T> next;
     5  
     6     public Node(T data, Node<T> next) {
     7         this.data = data;
     8         this.next = next;
     9     }
    10  
    11     public T getData() { return data; }
    12     // ...
    13 }

    编译器会使用第一个限定类,Comparable替换限定参数类型T:

     1 public class Node {
     2  
     3     private Comparable data;
     4     private Node next;
     5  
     6     public Node(Comparable data, Node next) {
     7         this.data = data;
     8         this.next = next;
     9     }
    10  
    11     public Comparable getData() { return data; }
    12     // ...
    13 }

    同样,泛型方法也可以擦除。规则类似,不细说。

    类型擦除的影响和桥方法

    有时候类型擦除会引起无法预知的情况。比如:

    给定以下两个类:

     1 public class Node<T> {
     2  
     3     public T data;
     4  
     5     public Node(T data) { this.data = data; }
     6  
     7     public void setData(T data) {
     8         System.out.println("Node.setData");
     9         this.data = data;
    10     }
    11 }
    12  
    13 public class MyNode extends Node<Integer> {
    14     public MyNode(Integer data) { super(data); }
    15  
    16     public void setData(Integer data) {
    17         System.out.println("MyNode.setData");
    18         super.setData(data);
    19     }
    20 }

    考虑以下代码: 

    1  MyNode mn = new MyNode(5);
    2 Node n = mn;            // 原生类型 – 编译器会给出未检查警告
    3 n.setData("Hello");     
    4 Integer x = mn.data;    // 会引发抛出ClassCastException

    类型擦出后,代码变成

    1 MyNode mn = new MyNode(5);
    2 Node n = (MyNode)mn;         //原生类型 – 编译器会给出未检查警告
    3 n.setData("Hello");
    4 Integer x = (String)mn.data; //会引发抛出ClassCastException
     1 public class Node {
     2  
     3     public Object data;
     4  
     5     public Node(Object data) { this.data = data; }
     6  
     7     public void setData(Object data) {
     8         System.out.println("Node.setData");
     9         this.data = data;
    10     }
    11 }
    12  
    13 public class MyNode extends Node {
    14  
    15     public MyNode(Integer data) { super(data); }
    16  
    17     public void setData(Integer data) {
    18         System.out.println("MyNode.setData");
    19         super.setData(data);
    20     }
    21 }

    类型擦除后,方法的签名已经不匹配。Node 方法变成setData(Object),MyNode方法变成setData(Integer)。MyNode setData方法已经不是覆盖Node setData方法。

    为了解决这个问题,维持泛型类型的多态性,java编译器会生成一个桥方法:

     1 class MyNode extends Node {
     2  
     3     // 编译器生成的桥方法
     4     //
     5     public void setData(Object data) {
     6         setData((Integer) data);
     7     }
     8  
     9     public void setData(Integer data) {
    10         System.out.println("MyNode.setData");
    11         super.setData(data);
    12     }
    13  
    14     // ...
    15 }
  • 相关阅读:
    【译】可扩展前端2  —  常见模式
    【译】可扩展前端1  —  架构基础
    【译】The Clean Architecture
    获取页面元素位置
    vue高价组件的使用
    gif动态图片转精灵图
    消除 transition 闪屏
    移动端 -- 如何去掉元素被触摸时产生的半透明灰色遮罩?
    解决手机移动端触屏版web页面长时间按住页面出现闪退的问题
    移动端滑动慢,卡顿
  • 原文地址:https://www.cnblogs.com/linghu-java/p/10031239.html
Copyright © 2020-2023  润新知