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

Переопределение общего метода с помощью общего в Java

Анжелика Лангер говорит в своем FAQ о дженериках (см. Technicalities.FAQ822):

Если методы имеют параметры типа с разными границами, то они не переопределяйте, потому что методы имеют подписи, которые не являются переопределить-эквивалент. Помните, что границы параметров типа являются частью общая сигнатура метода.

Пример (общих методов подтипа, перегружающих общий супертип методы; не рекомендуется):

class Super {
   public <T> void set( T arg) { ... }
   public <T> T get() { ... }
}
class Sub extends Super {
   public <S extends Number > void set( S arg) { ... } // overloads
   public <S extends Number > S get() { ... }         // overloads
}

Я не понимаю, почему метод get перегружен в классе Sub. Для того, что я знаю, это должна быть ошибка времени компиляции, потому что get имеет ту же подпись как в Sub, так и Super (тип возврата не является частью этого).

Что еще меня путает, так это то, что IDE, который я использую для тестирования кода (IntelliJ IDEA 14.0.3), выделяет get в Sub как ошибку компиляции со следующим сообщением:

'get()' в 'Sub' столкновения с 'get()' в 'Super'; оба метода имеют одно и то же стирание, но не переопределяют другой.

Но когда я запускаю программу, она компилируется и выполняется без проблем. Я полагаю, что есть некоторая ошибка в IntelliJ, когда она анализирует код, и что правильно, что говорит Анжелика в своем FAQ. Но я не могу уловить смысл.

4b9b3361

Ответ 1

Согласно JLS, подпись метода не включает тип возвращаемого значения, а только имя метода и тип его параметров. Это означает, что при компиляции Super и Sub ошибка компиляции должна возвращаться, поскольку Sub.get() имеет то же стирание, что и Super.get(), но не переопределяет и не перегружает Super.get(). Он не может переопределить, потому что ограниченный тип X extends Number не является подтипом типа X, и он не может перегружать, потому что тип возврата не является частью сигнатуры метода. Sub.set перегружает Super.set в этом случае.

Что вы можете скомпилировать и запустить. Если вы используете Java 6, в Java 6 есть известный bug, который бы скомпилировал Super и Sub. В Java 7 это исправлено и не будет разрешено.

Ответ 2

Я думаю, что стирание public <S extends Number> S get() не такое же или ковариантное, что и у public <T> T get().

Это компилируется и запускается:

class B {
    public <T> T get() {
        return null;
    }

}

class A extends B {
    @Override
    public <S> S get() {
        return null;
    }

Это не означает:

class B {
        public <T> T get() {
            return null;
        }

    }

    class A extends B {
        @Override
        public <S extends Number> S get() {
            return null;
        }

Ответ 3

Из того, что я понимаю, почему это перегрузка вместо переопределения - это потому, что компилятор не считает это переопределяющим эквивалентом. Далее в FAQ Angelika Langer вы видите, что он обсуждает, почему переопределение метода не так, как ожидалось.

http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ051

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