Что такое "переопределенная эквивалентность" и как это связано с @Override? - программирование
Подтвердить что ты не робот

Что такое "переопределенная эквивалентность" и как это связано с @Override?

Считая Javadoc для аннотации @Override, я наткнулся на следующее правило:

Если метод аннотируется с помощью этого компиляторы типа аннотации должны генерировать сообщение об ошибке если не выполняется хотя бы одно из следующих условий:

  • Метод переопределяет или реализует метод, объявленный в супертипе.
  • Метод имеет подпись, переопределяющую эквивалентную любому общедоступному методу объявлен в Object.

Я понимаю, в первую очередь, но я не уверен во втором.

Что означает "переопределить-эквивалент"? Как в этом отношении существуют специальные методы Object? И почему это не распространяется на первый критерий?

Заметка об обновлении: это относится только к документации по Java 7. Java 6 doc ничего не говорит о переопределении-эквивалентности. Почему изменение?


<сильного > Update:

После консультации с JLS (Раздел 8.4.2) я нашел следующее объяснение переопределенной эквивалентности:

Подпись метода m1 является подсигналом сигнатуры метода m2, если либо:

  • m2 имеет ту же подпись, что и m1, или
  • подпись m1 совпадает с стиранием (§4.6) подписи m2.

Две сигнатуры метода m1 и m2 эквивалентны переопределению, если либо m1 является подсигнала m2 или m2 является поднаклейкой m1.

Насколько я могу судить, это отвечает на первый вопрос ( "Что это значит?" ) и третий вопрос ( "Почему первое условие не покрывает это?" ).

Если я правильно понял (пожалуйста, сообщите мне, если я этого не сделаю!), есть только один случай, когда два метода переопределяют эквивалент и которые не подпадают под первое условие исходного вопроса. Это тот случай, когда стирание подписи метода подкласса совпадает с сигнатурой метода суперкласса, но не наоборот.

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

public class Foo {
    @Override
    public <T> boolean equals(Object obj) {
        return true;
    }
}

Конечно, этот класс не компилируется, потому что метод фактически не переопределяет метод equals и, таким образом, сталкивается с ним. Но я также все еще получаю сообщение об ошибке для использования аннотации @Override. Я ошибаюсь, полагая, что это допустимый пример второго условия использования @Override? Или компилятор генерирует эту ошибку, несмотря на то, что не требуется?

4b9b3361

Ответ 1

Причиной этого является возможность использования аннотации @Override в интерфейсах, которые не наследуются от Object, но неявно объявляют все общедоступные методы из Object (см. Элементы интерфейса JLS раздела 9.2). Таким образом, вы можете объявить интерфейс следующим образом:

interface Bar { @Override int hashCode(); }

Однако вам не будет разрешено объявить следующий интерфейс:

interface Quux { @Override Object clone(); }

поскольку метод clone() неявно объявлен в интерфейсе (это не public).

Это описано в JLS разделе 9.6.3.4 @Override (Javadoc для @Override все еще относится к старому номеру раздела)

Ответ 2

Ваш вопрос в основном является вопросом дизайна, и JLS объясняет его:

"Понятие поднаборки предназначено для выражения отношения между двумя методами, чьи подписи не идентичны, но в которых можно переопределить другое. В частности, он позволяет использовать метод, подпись не использует общие типы для переопределения любых генерируемых версия этого метода. Это важно для разработчиков библиотек могут свободно генерировать методы независимо от клиентов, которые определяют подклассы или подинтерфейсы библиотеки."

Ваш код не является допустимым примером этого, см. ниже приведенный ниже код:

public class SubSignatureTest extends SignatureTest {

    @Override
    public List test(Collection p) {
        return null;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

    }

}

class SignatureTest {

    public <T> List<T> test(Collection<T> t) {
        return null;

    }
}

Вся точка заключается в том, что подпись суперкласса и подкласса должна быть такой же после стирания.

EDIT: Когда мы говорим об эквивалентной эквивалентности, родительский класс должен иметь общий метод, а дочерний класс должен иметь не общий метод. Вот пример, чтобы объяснить это. Код Below не будет работать, потому что у дочернего класса есть общий метод. На мгновение допустим, что java разрешило, что тогда вызов в основном методе всегда будет терпеть неудачу:

class A{
       public int compareTo(Object o){
               return 0;
       }
}

class B extends A implements Comparable<B>{
       public int compareTo(B b){
               return 0;
       }

       public static void main(String[] argv){
               System.out.println(new B().compareTo(new Object()));
       }
}

В классе B метод будет таким же после компиляции:

public int compareTo(Object x){
    return compareTo((B)x);
  }

Это означает, что это всегда ошибка: new B().compareTo(new Object()). Поэтому java не позволит дочернему классу иметь общий метод, если родительский класс имеет не общий метод. Таким образом, вы не можете определить методы переопределения эквивалентности для класса объектов.

Надеюсь, что это прояснится.

Я использовал сообщение http://lists.seas.upenn.edu/pipermail/types-list/2006/001091.html для справки, у него есть намного больше деталей.