私は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
を返します。
String
とLong
を明示的に混合しているため、2行目は期待どおりにコンパイルされません。
3行目はコンパイル、実行、falseを返しますが、どのように動作するか理解できません。コンパイラ/ JVMはT
をObject
としてインスタンス化しますか? (また、この宣言された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()
。
はい、Object
はT
の選択肢であり、コンパイルを許可します。概念的には、コンパイラinfersT
の型。それが特に推測するものは重要ではありません-それが推測できる限りあるタイプはT
に対して動作し、それからコンパイルします。推定された型はコンパイルされたコードに影響を与えないため、その型は何でも構いません。