2013-04-08 2 views
25

Я изучаю Java родовую особенность, и я не знаю, как объяснить третью строчку в следующем main метода:вызываете методы Generic 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.

Вторая строка не компилируется, как ожидалось, потому что я явно смешиваю String и Long.

Третья строка компилирует, запускает и возвращает false, но я не уверен, как это работает: компилятор/JVM создает тип параметра T как Object? (Также был бы способ получить этот объявленный тип T - это время выполнения?)

Спасибо.

ответ

9

Ответ, кажется, выходит за пределы ответов @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.

+1

Просто обратите внимание на это здесь, даже если вы ссылаетесь на него; явление называется «стиранием типа». –

+0

Да. В принципе, Java реализует дженерики как зацикливаемое приведение типов. Например, 'ArrayList ' внутренне считает каждый элемент 'Object', но автоматически возвращает его в' String', когда вы его используете. –

20

Общий унаследованный тип String и Long: Object.

Когда вы запускаете эту функцию как Util.<String>compare(, компилятор рассчитывает найти два строковых ввода и дает ошибку, если это не так. Однако запуск без <String> приводит к использованию ближайшего общего унаследованного типа - в данном случае - Object.

Таким образом, когда compare принимает t1 и t2, они были отлиты в Object, и код работает нормально.

Чтобы получить фактический тип во время выполнения, вы используете ту же технику, что и с любым другим объектом: getClass(), который унаследован от класса Object.

+1

Также для второй части (способ получения объявленного типа T во время выполнения) найдите «Reified Generics» в Google. См. Gafter.blogspot.com/2006/11/reified-generics-for-java.html – prashant

+0

Метод getClass(), примененный, скажем, t1, вернет String, потому что это тип среды выполнения, не так ли? –

+0

Выполняя больше исследований, я наткнулся на эту интересную запись в блоге: [токены супертекста] (http://gafter.blogspot.kr/2006/12/super-type-tokens.html). –

2

Да, Object - это выбор для T, который позволит ему скомпилировать. По идее, компилятор сигнализирует a тип для T. То, что это особенно важно, не имеет значения - до тех пор, пока он может сделать вывод, что некоторый тип будет работать для T, затем он компилируется. Не имеет значения, что такое предполагаемый тип, поскольку он не влияет на скомпилированный код.