Стирание типа и перегрузка в Java: почему это работает? - PullRequest
17 голосов
/ 03 апреля 2011

У меня есть следующий код:

public class Pair< T, U > {
    public T first;
    public U second;
}
public class Test {
    public int method( Pair< Integer, Integer > pair ) {
        return 0;
    }
    public double method( Pair< Double, Double > pair ) {
        return 1.0;
    }
}

Это на самом деле компилируется и работает, как и следовало ожидать. Но если возвращаемые типы сделаны одинаковыми, это не скомпилируется, так как ожидаемое «name clash: method (Pair) и method (Pair) имеют одинаковое стирание»

Учитывая, что возвращаемый тип не является частью сигнатуры метода, как возможна эта перегрузка?

Ответы [ 4 ]

9 голосов
/ 03 апреля 2011

Рассмотрим следующие 4 метода

           Java code                        bytecode

m1:    Byte f(List<Byte> list)           f List -> Byte
m2:    Long f(List<Byte> list)           f List -> Long
m3:    Byte f(List<Long> list)           f List -> Byte
m4:    Long f(List<Long> list)           f List -> Long

В соответствии с текущей спецификацией языка Java,

  • m1 и m2 не могут сосуществовать, равно как и m3 и m4. потому что они имеют одинаковые типы параметров.

  • m1 и m3 могут сосуществовать, как и m1 и m4. потому что они имеют разные типы параметров.

Но javac 6 допускает только m1 + m4, но не m1 + m3. Это связано с представлением методов в виде байт-кода, которое включает типы возвращаемых данных. Следовательно, m1 + m4 в порядке, но не m1 + m3.

Это ошибка, из-за которой спецификации Java и JVM не видны с глазу на глаз. Не существует «правильного» пути для javac.

Хотя это отстой, хорошая новость заключается в том, что перегрузка - это тщеславие, а не необходимость. Мы всегда можем использовать разные, более описательные и разные имена для этих методов.

6 голосов
/ 03 апреля 2011

Перегрузка выполняется во время компиляции.

Хотя общие параметры стираются во время выполнения, они все еще доступны компилятору для устранения перегрузок.

2 голосов
/ 03 апреля 2011

Подпись метода Java на самом деле включает тип возвращаемого значения;если вы когда-либо работали с JNI, вы видели дескрипторы типов, такие как (LPair;) D и (LPair;) I .Этот последний символ обозначает типы возврата двух ваших методов.Хотя правило языка Java заключается в том, что параметры должны различаться для двух перегруженных методов, формат файла класса может фактически отличать методы на основе только их возвращаемых типов.Когда есть общая информация о типах, позволяющая компилятору разрешать перегрузки на основе их аргументов, тогда, пока возвращаемые типы различны, у стираний будут разные подписи, и все это прекрасно работает.Однако если типы возвращаемых данных совпадают, то после удаления методы имеют одинаковую сигнатуру, файл класса не может определить разницу, и у вас возникла проблема.

0 голосов
/ 03 апреля 2011

Я считаю, что вы на самом деле смотрите на неправильный набор текста.Говоря, что первый метод принимает Pair, вы даете ему очень специфический тип.Это как сказать метод (String, String).Второй метод пары подобен методу (Person, Person).Это тоже очень специфично на уровне набора текста.Если вы измените ваши методы на методы (Pair <T,U>, Pair <T,U>) и получите их дважды, вы прервете компиляцию.

Итак, короткий ответ: поскольку вы строго ввели «Пара», чтобы обозначить две разные вещи, «Дженерики» не играют, просто введите правило.

...