Стирание выполняется по-разному для подписи метода и метода?

Я наткнулся на пример, который предполагает, что стирание выполняется по-разному в сигнатуре и методе, но я не знаю, почему и как. JLS §8.4.8.3 указывает:

Это ошибка времени компиляции, если объявление типа T имеет метод-член m1 и существует метод m2, объявленный в T, или супертип T, для которого выполняются все следующие условия:

  1. m1 и m2 имеют одно и то же имя.
  2. м2 доступен с Т.
  3. Подпись m1 не является дополнительной подписью (§8.4.2) подписи m2.
  4. Сигнатура m1 или какого-либо метода, который m1 переопределяет (прямо или косвенно), стирается так же, как сигнатура m2 или какого-либо метода, который m2 переопределяет (прямо или косвенно).

Ошибка времени компиляции приведен пример :

class C<T> {
    T id (T x) {...}
}
class D extends C<String> {
    Object id(Object x) {...}
}

Объяснение:

Это незаконно, поскольку D.id(Object) является членом D, C.id(String) объявлен в супертипе D и:

  • Два метода имеют одинаковое имя, идентификатор
  • C.id(String) доступен для D
  • Подпись D.id(Object) не является подписью C.id(String)
  • Два метода имеют одинаковое стирание

Первые два пункта очевидны, но я не понимаю последние два пункта объяснения. Как два метода могут иметь одинаковое стирание, если выполняется третья точка? С третьей точки кажется, что стирание подписи выполняется с использованием метода параметризованного класса C‹String› (т.е. id(String) вместо id(T)). Если это так, то два метода должны иметь разные стирания, но пример предполагает, что стирание метода выполняется для непараметризованного класса. Итак, как на самом деле применяется стирание к сигнатуре и методу?


person Hin    schedule 22.05.2013    source источник


Ответы (1)


Стирание означает, что все вхождения универсального типа T (String в случае C) заменяются на Object (+ необходимые приведения типов). Поскольку таким образом информация о типе теряется, в примере возникает конфликт — JVM не сможет решить, какой метод вызывать.

редактировать (это неправильно): afaik: субсигнатура — это метод, который принимает совместимый тип (например, супертип String) и/или возвращает ковариантный тип.

Я попробовал это, и это сбивает с толку, но пришел к такому объяснению: компилятор не стирает, а заменяет общие подписи. Поэтому при D extends C<String> сигнатура переопределенного C<String>.id становится: String id(String x). Ясно, что метод D id имеет другую сигнатуру, а также другое стирание (поскольку String не является универсальным типом). Следовательно, подпись D.id не является подписью C. (Соответствует правилу 3)

С другой стороны, стирание C<T>.id(T x) равно Object id(Object x) и идентично стиранию D.id(Object x). (Соответствует правилу 4)

После этого было бы правильно переопределить id, если бы можно было сохранить согласованность подписей и стираний. И, по-видимому, это возможно (хотя и не очень полезно):

class Foo<T> {
   public void set(final T thing) {}
}

class Bar<T> extends Foo<T> {
   @Override
   public void set(final Object thing) {}
}

Это компилируется в eclipse без предупреждения.

person Pyranja    schedule 22.05.2013