web-dev-qa-db-ja.com

Javaジェネリックメソッドの呼び出し

私はJavaジェネリック機能を勉強しています。次のmainメソッドの3行目を説明する方法がわかりません:

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の型を実行時に取得する方法はありますか?)

ありがとうございました。

答えは@Telthienと@newacctの答えを超えているようです。私は自分自身で以下の違いを「見る」ことに興味がありました。

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

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

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

暗黙的なタイピング。

これら2つの前の行のバリエーションを使用して、いくつかの実験を行いました。これらの実験は、 anonymous/local class trick を使用する以外に、コンパイラーはコンパイル中に型をチェックしますが、生成されたバイトコードはObjectのみを参照します。最初の行。

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

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

mainメソッドのバイトコードは次のようになります。

  // 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[]

別の質問/回答で で説明されているように、実際には、すべての呼び出しは常にObjectを正式なパラメーター型として持つメソッドに対するものです。結論として、コンパイラーは、明示的な型引数(最初の行)または暗黙的な型引数があるかどうかに関係なく、生成されたバイトコードに常にObjectを使用しますが、オブジェクトはObject

StringおよびLongの共有継承型はObjectです。

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

したがって、compareが_t1_および_t2_を受け入れる場合、それらはObjectとしてキャストされ、コードは正常に実行されます。

実行時に実際の型を取得するには、他のオブジェクトで使用するのと同じ手法を使用します:Objectクラスから継承されるgetClass()

21
Aza

はい、ObjectTの選択肢であり、コンパイルを許可します。概念的には、コンパイラinfersTの型。それが特に推測するものは重要ではありません-それが推測できる限りあるタイプTに対して動作し、それからコンパイルします。推定された型はコンパイルされたコードに影響を与えないため、その型は何でも構いません。

2
newacct