Подтвердить что ты не робот

Вызов общих методов Java

Я изучаю общую функцию Java, и я не уверен, как объяснить третью строку в следующем методе main:

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 - время выполнения?)

Спасибо.

4b9b3361

Ответ 1

Ответ, кажется, выходит за пределы ответов @Telthien и @newacct. Мне было любопытно "видеть" для себя разницу между:

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

с типизацией типизации и:

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

с неявной типизацией.

Я провел несколько экспериментов, используя вариации на этих двух предыдущих строках. Эти эксперименты показывают, что, несмотря на использование анонимного/локального трюка класса, компилятор проверяет типы во время компиляции, но сгенерированные байт-коды ссылаются только на 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 как формальные типы параметров, как объяснялось в другом вопросе/ответе. Чтобы скрыть, компилятор всегда использует Object для сгенерированных байт-кодов, неважно, есть ли аргумент типа экспликации (первая строка) или неявный аргумент типа, но что объекты могут иметь общий суперкласс, отличный от Object.

Ответ 2

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

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

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

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

Ответ 3

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