2013-06-24 17 views
8

は、Javaのジェネリックのこの単純な例を見てください:リスト<T>はリスト<T>と等しくないのですか?

class List<T> { 
    T head; 
    List<T> next; 
} 

class A<T> { 
    List<T> l; 

    public <T> int length() { 
     List<T> l = this.l; 
     int c = 1; 
     while (l.next != null) { 
      c++; 
      l = l.next; 
     } 
     return c; 
    } 

    public static void main(String[] args) { 
     A<Integer> a = new A<Integer>(); 
     a.l = new List<Integer>(); 
     a.l.head = 123; 
     a.l.next = new List<Integer>(); 
     a.l.next.head = 432; 
     System.out.println("list length: " + a.length()); 
    } 
} 

それは型に互換性がないと主張し、コンパイルエラーを取得し、まだ2つの変数が同じ型であることを主張:

$ javac A.java && java A 
A.java:10: incompatible types 
found : List<T> 
required: List<T> 
     List<T> l = this.l; 
         ^
1 error 

length()の最初の行をList<T> l = (List<T>)(Object)this.l;に変更すると、それは機能します。どうして?

+1

名前がこのような共通のJDKクラス(この場合はjava.util.List)と競合するクラスを作成することは悪い考えです。混乱がたくさんあります。 – yshavit

答えて

19

あなたはこのラインでジェネリッククラス内のジェネリックメソッドを宣言した:

public <T> int length() { 

この<T>は、あなたのクラスの<T>とは異なります。 JLS Section 6.3によれば:

クラスの型パラメータの範囲は、(8.1.2)されたクラス宣言の型パラメータ セクションは、クラス宣言の任意 スーパークラス又はスーパーインターフェースの型パラメータ部、本体は です。

<T>をメソッドに再宣言する必要はありません。クラスのtypeパラメータはすでにスコープ内にあります。

public int length() { 

あなたのキャストが動作する理由を理解するには:

List<T> l = (List<T>)(Object)this.l; 

することができますあなたの方法は、別の<T>を宣言し、単にあなたのクラスの<T>を使用していない、あなたのクラスのジェネリック型パラメータ<T>を使用するには

任意のオブジェクトをObjectにキャストします。次に結果をList<T>にキャストします。あなたはいつでもあなたが望むものにキャストすることができます。実行時に実際にListでない場合、Javaは単に実行時にClassCastExceptionをスローします。しかし、この<T>が元の<T>であることを保証することができないため、コンパイラはチェックされていない操作または安全でない操作を使用するという警告も表示します。

<T>の違いを説明するために、あなたは、メソッドのジェネリック型パラメータとして<U>を使用し、同じ結果を得ることができます。これは、同じタイプの安全警告でコンパイル

public <U> int length() { 
    List<U> l = (List<U>)(Object)this.l; 

を。

実際にタイプの安全性が保証されている場合は、@SuppressWarnings("unchecked")を使用してメソッドに注釈を付けることができます。しかしここでは、ジェネリック型パラメータをメソッドから完全に削除し、クラスのtypeパラメータを使用しています。

関連する問題