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 }