Ответ, кажется, выходит за пределы ответов @Telthien и @newacct. Мне было интересно "увидеть" для себя разницу между:
System.out.println(Util.<String>compare("a", "b"));
с явно типизации, а также:
System.out.println(Util.compare(new String(""), new Long(1)));
с неявной типизации.
Я провел несколько экспериментов, используя вариации на этих двух предыдущих строках. Эти эксперименты показывают, что, не используя anonymous/local class trick, компилятор проверяет типы во время компиляции, но сгенерированные байт-коды ссылаются только на Object
, даже в случае первой строки.
Следующая часть кода показывает, что приемы типа могут быть выполнены безопасно вплоть до Object
даже в случае аргумента типа объяснения <String>
.
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
как формальные типы параметров, как описано in another question/answer. Чтобы скрыть, компилятор всегда использует Object
для сгенерированных байт-кодов, неважно, есть ли аргумент типа экспликации (первая строка) или неявный аргумент типа, но что объекты могут иметь общий суперкласс, отличный от Object
.
Просто обратите внимание на это здесь, даже если вы ссылаетесь на него; явление называется «стиранием типа». –
Да. В принципе, Java реализует дженерики как зацикливаемое приведение типов. Например, 'ArrayList' внутренне считает каждый элемент 'Object', но автоматически возвращает его в' String', когда вы его используете. –