JAva汎用型のタイプ消去とブリッジ方法

15296 ワード

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 {
 2  
 3     private T data;
 4     private Node next;
 5  
 6     public Node(T data, Node 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  }

 
次の例では、汎用ノードクラスでは、限定タイプパラメータが使用されます.
 1  public class Nodeextends Comparable> {
 2  
 3     private T data;
 4     private Node next;
 5  
 6     public Node(T data, Node next) {
 7         this.data = data;
 8         this.next = next;
 9     }
10  
11     public T getData() { return data; }
12     // ...
13 }

コンパイラは、限定パラメータタイプTをComparableで置換する最初の限定クラスを使用します.
 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 }

同様に、汎用的な方法でも消去できます.ルールが似ていて、詳しくは言わない.
タイプ消去の影響とブリッジ方法
タイプ消去で予知できない場合があります.例:
次の2つのクラスを指定します.
 1 public class Node {
 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 {
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 }