2013-04-08 26 views
25

私はJavaのジェネリック機能を勉強していると私はどのように、次のmainの方法で三行目を説明することを確認していない:最初の行は、コンパイルし実行し、リターン(期待通りに)の呼び出しJavaのジェネリックメソッド

public class Example4 { 
    public static void main(final String[] args) { 
     System.out.println(Util.<String>compare("a", "b")); 
     System.out.println(Util.<String>compare(new String(""), new Long(1))); 
     System.out.println(Util.compare(new String(""), new Long(1))); 
    } 
} 

class Util { 
    public static <T> boolean compare(T t1, T t2) { 
     return t1.equals(t2); 
    } 
} 

false

StringLongを明示的に混ぜているので、2行目は期待どおりにコンパイルされません。

第3行はfalseをコンパイル、実行、返しますが、どのように動作するのか分かりません。コンパイラ/ JVMはパラメータタイプTObjectとしてインスタンス化しますか? (また、この宣言されたタイプのTを取得する方法は、実行時ですか?

ありがとうございます。

答えて

9

答えは@Telthienと@newacctの回答を超えているようです。暗黙の型と

System.out.println(Util.compare(new String(""), new Long(1))); 

:明示的タイピング、およびと

System.out.println(Util.<String>compare("a", "b")); 

:私は自分自身のための違いを "見る" ことが気になっていました。

私はいくつかの実験を行いました。これらの2つの前の行のバリエーションを使用しました。これらの実験では、anonymous/local class trickを使用しないと、コンパイラはコンパイル時に型をチェックしますが、生成されるバイトコードは最初の行の場合でもObjectを参照するだけです。

次のコードは、明示的な型引数<String>の場合でも、型キャストがObjectまで安全に実行できることを示しています。

// Method descriptor #15 ([Ljava/lang/String;)V 
    // Stack: 7, Locals: 1 
    public static void main(java.lang.String[] args); 
    0 getstatic java.lang.System.out : java.io.PrintStream [16] 
    3 new ca.polymtl.ptidej.generics.java.Util44 [22] 
    6 dup 
    7 invokespecial ca.polymtl.ptidej.generics.java.Util44() [24] 
    10 ldc <String "a"> [25] 
    12 ldc <String "b"> [27] 
    14 invokevirtual ca.polymtl.ptidej.generics.java.Util44.compare(java.lang.Object, java.lang.Object) : boolean [29] 
    17 invokevirtual java.io.PrintStream.println(boolean) : void [33] 
    20 getstatic java.lang.System.out : java.io.PrintStream [16] 
    23 new ca.polymtl.ptidej.generics.java.Util44 [22] 
    26 dup 
    27 invokespecial ca.polymtl.ptidej.generics.java.Util44() [24] 
    30 new java.lang.String [39] 
    33 dup 
    34 ldc <String ""> [41] 
    36 invokespecial java.lang.String(java.lang.String) [43] 
    39 new java.lang.Long [46] 
    42 dup 
    43 lconst_1 
    44 invokespecial java.lang.Long(long) [48] 
    47 invokevirtual ca.polymtl.ptidej.generics.java.Util44.compare(java.lang.Object, java.lang.Object) : boolean [29] 
    50 invokevirtual java.io.PrintStream.println(boolean) : void [33] 
    53 return 
     Line numbers: 
     [pc: 0, line: 24] 
     [pc: 20, line: 25] 
     [pc: 53, line: 26] 
     Local variable table: 
     [pc: 0, pc: 54] local: args index: 0 type: java.lang.String[] 

それは実際にin another question/answerを説明したようにすべてのコールが、仮パラメータ型としてObjectと方法に常にあることを意味します:main方法の

public final class Example44 { 
    public static void main(final String[] args) { 
     System.out.println(new Util44<String>().compare("a", "b")); 
     System.out.println(new Util44().compare(new String(""), new Long(1))); 
    } 
} 

final class Util44<T> { 
    private T aT; 
    public boolean compare(T t1, T t2) { 
     System.out.println(this.aT); 
     // I was expecting the second and third assignments to fail 
     // with the first invocation because T is explicitly a String 
     // and then to work with the second invocation because I use 
     // a raw type and the compiler must infer a common type for T. 
     // Actually, all these assignments succeed with both invocation. 
     this.aT = (T) new String("z"); 
     this.aT = (T) new Long(0); 
     this.aT = (T) new Object(); 
     return t1.equals(t2); 
    } 
} 

バイトコードは次のようになり。暗黙の型の引数(1行目)または暗黙の型の引数があっても、オブジェクトがObjectとは異なる共通のスーパークラスを持つことができるかどうかに関わらず、コンパイラは常に生成されたバイトコードにObjectを使用します。

+1

あなたがリンクしているにもかかわらず、これをここに書き留めておきます。この現象を「タイプ消去」といいます。 –

+0

うん。基本的に、Javaはジェネリックを背中の型キャストとして実装しています。たとえば、 'ArrayList 'は、内部的にすべての要素を 'Object'と見なしますが、使用すると自動的に' String'にキャストします。 –

20

StringLongの共有継承型はObjectです。

この関数をUtil.<String>compare(として実行すると、コンパイラは2つの文字列入力を検出し、そうでないときにエラーを返します。ただし、<String>を付けずに実行すると、最も近い共有継承型(この場合はObject)が使用されます。

comparet1t2を受け入れると、それらはObjectとしてキャストされ、コードは正常に動作します。

実行時に実際のタイプを取得するには、他のオブジェクトで使用するのと同じテクニックを使用します。getClass()Objectクラスから継承されています。

+1

また、2番目の部分(実行時にTの宣言された型を取得する方法)では、Googleで「Reified Generics」を検索します。 gafter.blogspot.com/2006/11/reified-generics-for-java.htmlを参照してください – prashant

+0

getClass()メソッドは、たとえばt1に適用され、実行時の型であるためStringを返します。 –

+0

もっと研究しているうちに、私はこの興味深いBlogのエントリー[super type tokens](http://gafter.blogspot.kr/2006/12/super-type-tokens.html)を見つけました。 –

2

はい、の場合は、Objectを選択してコンパイルできます。概念的には、コンパイラは、のタイプを推定します。特に考えられるのは重要ではありません。の一部がTで動作し、それがコンパイルされることが推測できる限り重要です。それはコンパイルされたコードに影響を与えないので、推論された型が何であるかは関係ありません。

関連する問題