2016-11-29 12 views
4

Java GenericsチュートリアルとGenericsを扱うStackoverflowのスレッドはほとんど読んでいませんでしたが、それでも特定のケースを理解できませんでした。 はここにある:ここでは Java Generics - 生の型とパラメータ化された型変換

public class Box<T> 
{ 
    private T t; 

    public T getT() 
    { 
     return t; 
    } 

    public void setT (T t) 
    { 
     this.t = t; 
    } 

    public static void main (String[] args) 
    { 
     Box<Integer> intBox = new Box<Integer>(); 
     Box rawBox = intBox; 
     rawBox.setT("NBA"); 
     System.out.println(rawBox.getT()); 
     System.out.println(intBox.getT()); 
     /*1*/ //System.out.println(intBox.getT().toString()); 
    } 
} 

が契約、私は理解して最初の印刷、つまりだ、

System.out.println(rawBox.getT()); 

プリントNBA、rawBoxはボックスTの生タイプのものであり、それが私たち「を与える」ため、オブジェクト。

System.out.println(intBox.getT()); 

NBAを印刷します:私は得ることはありません何

は、第二の印刷です。 intBoxはジェネリック型(この場合はBox of Integers)です。つまり、getterメソッドはT型の値(この場合はInteger)を返さなければなりません。 (これはBox Tに与えられた引数型であるため)Integerに変換され、実行時にClassCastExceptionを発生させる必要がありますが、それは起こりません。私はそれをアンコメントしていたならば、それはClassCastExceptionが原因だろうから仕方、コメント数で

は/ /(文字列が整数にキャストすることはできません)ruuntimeで飼育されるように、混乱を追加し、私は「ドンそのことを理解してください

ありがとうございます。

答えて

1

タイプ消去が理由です。実行時には、すべてのBoxインスタンスは等しく、すべてが参照を含みます。ジェネリックスは、プログラマーであるタイプインフォメーションを伝え、プログラムの型を整えておくだけの利点があります。

2

Javaがチェックキャストを挿入する場所を推測するのが時々難しいです。一般的に、必要に応じて挿入するだけです。表示される動作を理解する最善の方法は、バイトコードをチェックすることです!

我々は(アンコメント/*1*/でコンパイルした後)javap -c Box.classを実行した場合、我々は、次を参照してください。

public static void main(java.lang.String[]); 
    Code: 
     ... 
     20: invokevirtual #8     // Method getT:()Ljava/lang/Object; 
     23: invokevirtual #9     // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 
     26: getstatic  #7     // Field java/lang/System.out:Ljava/io/PrintStream; 
     29: aload_1 
     30: invokevirtual #8     // Method getT:()Ljava/lang/Object; 
     33: invokevirtual #9     // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 
     36: getstatic  #7     // Field java/lang/System.out:Ljava/io/PrintStream; 
     39: aload_1 
     40: invokevirtual #8     // Method getT:()Ljava/lang/Object; 
     43: checkcast  #10     // class java/lang/Integer 
     46: invokevirtual #11     // Method java/lang/Integer.toString:()Ljava/lang/String; 
     49: invokevirtual #12     // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
     52: return 

あなたはJVMが実際に整数にキャストを必要とするだけの時間が43位(checkcast)であることをここで見ることができます。それで、それはinvokevirtual Integer.toString()です。 printlnObjectを、かかるため

println(#33)への呼び出しは、キャストを必要としないではないInteger(おそらくあなたはprintln(int)を呼んでいたと思ったが、そうでないなら)。そのため、JVMは必ずしも整数である必要はありません。

println(Object)を呼び出す代わりに、Integerを受け入れたメソッドを呼び出した場合は、代わりにClassCastExceptionが表示されます。例えば

、この:

... 
    print(intBox.getT()); 
} 

private static void print(Integer integer) { 
    System.out.println(integer); 
} 

はキャストを実行します。

26: aload_1 
    27: invokevirtual #8     // Method getT:()Ljava/lang/Object; 
    30: checkcast  #10     // class java/lang/Integer 
    33: invokestatic #11     // Method print:(Ljava/lang/Integer;)V 
+0

最初に、すばらしい答え、 Object型の引数を受け入れるprintlnメソッドを理解しました。 これは、printlnメソッドを呼び出さないと、次のステートメントを呼び出さない理由を説明していません。 intBox.getT(); ClassCastExceptionを発生させません:StringはIntegerにキャストできません。 その文はコンパイルされて正常に動作します。 intBoxがパラメータ化された型(Integer)の場合、そのgetterメソッドはIntegerを返し、StringからIntegerへの変換は であるため、getterメソッド自体から例外が発生するため、発生しません。 – Michael1

+0

@ Michael1:キャストの戻り値の型を強制するのは、ジェネリックメソッドの* caller *までです。インスタンス化された型ごとに別々の '.class'ファイルが生成されていないことを思い出してください。言い換えれば、 'Box.class'には、すべての型に対して動作する必要があるため、' Integer'にキャストを追加する場所がありません。パラメータの* erasure *のみを強制します(この場合、消去は 'Object'です)。次に、必要になったときに発信者がキャストを行う必要があります。 –

+0

[JLS 15.5](https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.5):*静的に既知のタイプが存在しない場合があります実行時に正確です。このような状況は、コンパイル時に未確認の警告を発生させるプログラムで発生する可能性があります。このような警告は、静的に安全であることが保証されていない操作に対応して与えられます。結果として、**プログラムの実行中に後で動的チェックが実行されると、実行時型エラーが発生する可能性があります。**。* –

0

コンパイラは非ジェネリッククラス参照を使用しないことを警告する理由です。実行時には、ジェネリックがバイトコードに保存されないので、intboxの型を知る能力がありません。

関連する問題