java 泛型的類型擦除和橋方法

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

在Java中,泛型的引入是爲了在編譯時提供強類型檢查和支持泛型編程。爲了實現泛型,Java編譯器應用類型擦除實現:java

       一、  用類型參數(type parameters)的限定(若是沒有就用Object)替換泛型類型中的全部類型參數。編程

       二、  須要保持類型安全的時候插入類型轉換(隱含插入)安全

       三、  在extened 泛型類型中生成橋方法來保證多態性oracle

   類型擦除確保不會爲已參數化了的類型(paramterized types)產生新類,這樣泛型能保證沒有運行時的負載。this

 

泛型類型擦除spa

      在類型擦除過程當中,java編譯器擦除全部類型參數,用它的限定或者Object(沒限定時)替換。code

     考慮下面的泛型類:  htm

 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替換它:  blog

 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 }
相關文章
相關標籤/搜索