Наследование универсального метода

Речь идет о следующем блоке кода:

public class SomeClass {
    public static class A {
        public void f(int x) {
            System.out.println("1");
        }

        public void f(Object x) {
            System.out.println("2");
        }
    }

    public static class B extends A {
        public <T> void f(T x) {   // *
            System.out.println("3");
        }
    }
}

Строка * не компилируется со следующей ошибкой:

Описание Путь к ресурсу Местоположение Тип Имя Конфликт: метод f (T) типа SomeClass.B имеет то же стирание, что и f (Object) типа SomeClass.A, но не отменяет его.

Чтобы избежать дублирования: я посмотрел на: Тип стирание, переопределение и обобщения, метод имеет такое же стирание, что и другой метод типа, и хотя я получил ответ (частично, потому что я все еще не полностью его понял), почему возникает ошибка компиляции (чтобы избежать двусмысленности из-за стирания типа? вот оно?) основная проблема в том, что я не понимаю, почему, если я переключаюсь между двумя методами f(Object x) и f(T x), код будет таким:

public class SomeClass {
    public static class A {
        public void f(int x) {
            System.out.println("1");
        }


        public <T> void f(T x) {
            System.out.println("3");
        }

    }

    public static class B extends A {
        public void f(Object x) {
            System.out.println("2");
        }
    }
}

Я не получаю сообщение об ошибке компиляции. Почему это происходит? В чем ключевое отличие, при котором для первого кода я получаю ошибку компиляции, а для второго - нет. Хотел бы кое-какие объяснения по этому поводу, спасибо!


person Mickey    schedule 23.07.2017    source источник
comment
По сути, это связано с этим ответом: Для переопределения с помощью методов экземпляра вам необходимо, чтобы метод переопределения был подзаводской переопределенный метод (JLS 8.4.8.1). void f(Object x) является дополнительной подписью <T> void f(T x), но не наоборот.   -  person Oliver Charlesworth    schedule 23.07.2017
comment
Возможный дубликат метода имеет такое же стирание, что и другой метод в тип   -  person Timothy Truckle    schedule 23.07.2017


Ответы (2)


Один из ответов на один из вопросов, которые вы связали, по сути, уже касается этого:

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

Чтобы разобраться в этом подробнее, JLS 8.4.8.1 говорит следующее:

Метод экземпляра m C, объявленный в классе C или унаследованный им, переопределяет из C другой метод m A, объявленный в классе A, если выполняются все следующие условия:

  • [...]

  • Подпись m C является дополнительной подписью (§8.4.2) подписи m A.

Подподпись определяется в JLS 8.4.2:

Подпись метода m 1 является дополнительной подписью сигнатуры метода m 2, если:

  • [...]

  • подпись m 1 такая же, как стирание (§4.6) подписи m 2.

Вы рассматриваете следующие две подписи:

  1. void f(Object x)
  2. <T> void f(T x)

Стирание №2 - это void f(Object x), поэтому №1 является его дополнительной подписью. Однако наоборот неверно.

person Oliver Charlesworth    schedule 23.07.2017
comment
Спасибо, наверное пропустил, так как отвечал на мой второй вопрос под всем текстом - person Mickey; 23.07.2017

Проблема здесь в стирании типа, как написано в ошибке. Это означает, что ваше ограничение / определение общего типа <T> в классе B рассматривается только компилятором для проверки типа. Однако то, что вы поместили <T>, не доступно в байтовом коде среды выполнения для разрешения метода.

Таким образом, посмотрите на свой public void <T> f(T x), когда тип будет удален - все, что останется, это Object (это просто то, что вы можете использовать в качестве первого параметра). Но тогда это та же сигнатура, что и в классе A - выдает ошибку.

Во втором случае это нормально, так как ваш метод класса B будет принимать любой объект - поэтому он, конечно же, примет любой <T>, который вы указали в подписи A.

person Michael Rose    schedule 23.07.2017