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

Способы и интерфейсы по умолчанию, расширяющие другие интерфейсы

Предположим, что существуют два интерфейса Interface1 и Interface2, где Interface2 продолжается Interface1.

interface Interface1 {

    default void method() {
        System.out.println("1");
    }

    // Other methods
}

interface Interface2 extends Interface1 {

    @Override
    default void method() {
        System.out.println("2");
    }

    // Other methods
}

Предположим, что я хочу создать класс, который реализует Interface2, но я хочу, чтобы method() была версией в Interface1. Если я напишу

class MyClass implements Interface1, Interface2 {

    public void method() {
        Interface1.super.method();
    }
}

Я получаю ошибку компиляции:

неудачный тип квалификатора при супервызове по умолчанию: избыточный интерфейс Интерфейс1 расширяется интерфейсом2

Это можно обойти, создав третий интерфейс:

interface Interface3 extends Interface1 {

    default void method() {
        Interface1.super.method();
    }
}

Тогда:

class MyClass implements Interface1, Interface2, Interface3 {

    public void method() {
        Interface3.super.method();
    }
}

Это компилируется отлично, и если я создаю новый MyClass и вызываю method(), вывод будет 1, как ожидалось.

Итак, мой вопрос заключается в том, что так легко обойти ограничение, которое вы можете написать только InterfaceName.super.method() для наиболее конкретного интерфейса в цепочке, в чем причина ограничения? Какие проблемы можно предотвратить, запретив вам сначала писать Interface1.super.method()?

4b9b3361

Ответ 1

Это точно адресовано JLS в 15.12.3. "Время компиляции Шаг 3: подходит ли выбранный метод?".

Если форма является TypeName. супер. Идентификатор [TypeArguments], затем:

  • [& hellip;]
  • Если TypeName обозначает интерфейс, пусть T будет объявлением типа, немедленно включающим вызов метода. Ошибка времени компиляции возникает, если существует метод, отличный от объявления времени компиляции, который переопределяет (§9.4.1) объявление времени компиляции из прямого суперкласса или прямого суперинтерфейса T.

JLS продолжает объяснять, почему правило находится на месте:

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

Таким образом, это более или менее существует специально, чтобы остановить вас от того, что вы пытаетесь сделать.

Но JLS также, похоже, подтверждает ваше обходное решение:

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