2016-01-26 9 views
7

Javaジェネリックのオラクルのトレイルの1つである「Effects of Type Erasure and Bridge Methods」を検討していましたが、私は説明を納得させることができませんでした。不思議なことに、コードをローカルでテストしたところ、トレイルが説明する動作を再現することさえできませんでした。ここに関連するコードは次のとおりです。Javaジェネリックに関するOracleのトレールの潜在的な問題

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); 
    } 
} 

オラクル・トレイルは、このコードスニペットのために次の動作を主張する:

MyNode mn = new MyNode(5); 
Node n = mn;   // A raw type - compiler throws an unchecked warning 
n.setData("Hello");  
Integer x = mn.data; // Causes a ClassCastException to be thrown. 

このコードスニペットは、型消去した後、次のようになります。

MyNode mn = new MyNode(5); 
Node n = (MyNode)mn;   // A raw type - compiler throws an unchecked warning 
n.setData("Hello"); 
Integer x = (String)mn.data; // Causes a ClassCastException to be thrown. 

ここで使われているキャストやその動作を理解できませんでした。

MyNode mn = new MyNode(5); 
Node n = mn;   // A raw type - compiler throws an unchecked warning 
n.setData("Hello");  // Causes a ClassCastException to be thrown. 
Integer x = mn.data; 

言い換えれば、JVMはStringsetData()で使用することを許可しません。私はローカルのJava 7でIntelliJのを使用してこのコードを実行しようとしたとき、私はこの動作を得ました。これは私には直感的なことであり、ジェネリックの理解に同意します。 MyNodemnIntegerで作成されているため、setData()の呼び出しをすべてIntegerにキャストして、型の安全性を確保する必要があります(Integerが渡されています)。

Oracleトレイルのこの明白なバグについて、誰かが何かを明らかにすることはできますか?

答えて

3

Oracleのページが誤って読み込まれました。あなたが最後まで読んでいれば、何が起こるかは、あなたが何を記述しているかということがわかります。

よく書かれたページではありません。著者は、「ああ、これが事実だとしたら、だれが起こっても、私たちはそれが事実ではないと思っているのですか」ということを意味するとき、「ああ、起こる」と言います。彼らはあまりにも自分の言語で緩んでいる。

ページブリッジメソッドのポイントは、予測動作(Generic Acutal Design + Implementationに基づく)が最初に「示唆」されたものであるときに、実際の動作がどのように観察されたかを説明することです。

+0

私はそれをあまりにも多くのクレジットを与えているページがその言語に緩んでいると言っ思う:あなたはMyNode.classファイルにjavap -pを実行する場合

、あなたがその実際に参照してくださいよ、それは2つのsetDataメソッドを持っています。つまり、n.setData( "Hello")という呼び出しは、実際にはコンパイラによって生成されたブリッジメソッドに当たっていて、スーパーメソッドを呼び出す前に渡されるすべてのオブジェクトを 'Integer'にキャストします。まあ、これは私が推測するすべてのことを、洞察力のおかげで説明します。 –

+1

私は悪いドキュメントについて嫌になる傾向がありますが、私は恐ろしいドキュメント、特にAPIの出版に慣れ親しんできました。皮肉なことに、若い頃Javaには最高のドキュメントがいくつかありました。今日...私は自分自身に私の軽蔑を維持しようとする。 – Adam

1

これは、トレイルで説明されています。

クラスNodeがコンパイルされた理論、では、その基本型TObjectに消去されます。

だから現実には、次にあなたがsetData(Integer data)独自のを持っている子クラスMyNodeを、作成

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; 
    } 
} 

のようなものにコンパイルされます。 Javaに関する限り、これはメソッドのオーバーロードであり、オーバーライドされません。各MyNodeオブジェクトは2つのメソッドを持っています。 1つはsetData(Object)で、それはNodeから継承され、もう1つはsetData(Integer)です。

あなたは生の型を使用し、あなたがIntegerでない任意の参照してsetDataを呼び出すのであれば基本的には、本のJavaの通常の解釈は、過負荷setData(Object)を呼び出すことであろう。

dataIntegerではなくObjectと宣言されているため、割り当てに問題はありません。この問題は、Integerの参照にデータを戻そうとするときにのみ発生します。 Javaのこのような単純な動作により、MyNodeオブジェクトは不適切なデータで「汚染されている」ことになります。

しかし、トレイルにあるように、コンパイラは「ブリッジ」メソッドを追加して、子クラスを直感的に思うように動作させます。 setData(Object)MyNodeに追加して、元の非安全の電話番号Node.setData(Object)を呼び出すことはできません。このオーバーライドブリッジメソッドでは、Integerへの明示的なキャストがあり、dataに非整数参照を割り当てることはできません。

実際にこのサンプルをコンパイルして実行したときの動作です。

class MyNode extends Node<java.lang.Integer> { 
    public MyNode(java.lang.Integer); 
    public void setData(java.lang.Integer); 
    public void setData(java.lang.Object); 
} 
関連する問題