Вопрос-ответ

Implementing two interfaces in a class with same method. Which interface method is overridden?

Реализация двух интерфейсов в классе с одним и тем же методом. Какой метод интерфейса переопределен?

Два интерфейса с одинаковыми именами методов и сигнатурами. Но реализованы одним классом, тогда как компилятор определит, какой метод для какого интерфейса?

Пример:

interface A{
int f();
}

interface B{
int f();
}

class Test implements A, B{
public static void main(String... args) throws Exception{

}

@Override
public int f() { // from which interface A or B
return 0;
}
}
Переведено автоматически
Ответ 1

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


Пример совместимости

Вот пример, где у вас есть interface Gift, у которого есть present() метод (например, вручение подарков), а также interface Guest, у которого также есть present() метод (например, гость присутствует, а не отсутствует).

Presentable johnny является одновременно Gift и a Guest.

public class InterfaceTest {
interface Gift { void present(); }
interface Guest { void present(); }

interface Presentable extends Gift, Guest { }

public static void main(String[] args) {
Presentable johnny = new Presentable() {
@Override public void present() {
System.out.println("Heeeereee's Johnny!!!");
}
};
johnny.present(); // "Heeeereee's Johnny!!!"

((Gift) johnny).present(); // "Heeeereee's Johnny!!!"
((Guest) johnny).present(); // "Heeeereee's Johnny!!!"

Gift johnnyAsGift = (Gift) johnny;
johnnyAsGift.present(); // "Heeeereee's Johnny!!!"

Guest johnnyAsGuest = (Guest) johnny;
johnnyAsGuest.present(); // "Heeeereee's Johnny!!!"
}
}

Приведенный выше фрагмент компилируется и запускается.

Обратите внимание, что необходим только один @Override !!!. Это потому, что Gift.present() и Guest.present() являются "@Overrideэквивалентными" (JLS 8.4.2).

Таким образом, johnny имеет только одну реализацию из present(), и не имеет значения, как вы относитесь к johnny, будь то как Gift или как a Guest, есть только один метод для вызова.


Пример несовместимости

Вот пример, в котором два унаследованных метода НЕ @Override эквивалентны:

public class InterfaceTest {
interface Gift { void present(); }
interface Guest { boolean present(); }

interface Presentable extends Gift, Guest { } // DOES NOT COMPILE!!!
// "types InterfaceTest.Guest and InterfaceTest.Gift are incompatible;
// both define present(), but with unrelated return types"
}

Это еще раз подтверждает, что наследование членов от interface должно подчиняться общему правилу объявления членов. Здесь мы имеем Gift и Guest define present() с несовместимыми типами возвращаемых значений: один void другой boolean. По той же причине, по которой вы не можете использовать void present() и a boolean present() в одном типе, этот пример приводит к ошибке компиляции.


Краткие сведения

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

Компилятору не нужно определять, какой метод для какого интерфейса предназначен, потому что, как только они определены как @Override-эквивалентные, это один и тот же метод.

Устранение потенциальных несовместимостей может быть сложной задачей, но это совсем другая проблема.

Ссылки

Ответ 2

Это было отмечено как дубликат этого вопроса https://javalang.ru/questions/24401064/understanding-and-solving-the-diamond-problems-in-java

Вам нужна Java 8, чтобы получить проблему множественного наследования, но это все еще не проблема diamon как таковая.

interface A {
default void hi() { System.out.println("A"); }
}

interface B {
default void hi() { System.out.println("B"); }
}

class AB implements A, B { // won't compile
}

new AB().hi(); // won't compile.

Как комментирует JB Nizet, вы можете исправить это мое переопределение.

class AB implements A, B {
public void hi() { A.super.hi(); }
}

Однако у вас нет проблем с

interface D extends A { }

interface E extends A { }

interface F extends A {
default void hi() { System.out.println("F"); }
}

class DE implement D, E { }

new DE().hi(); // prints A

class DEF implement D, E, F { }

new DEF().hi(); // prints F as it is closer in the heirarchy than A.
Ответ 3

Что касается компилятора, эти два метода идентичны. Будет одна реализация обоих.

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

Ответ 4

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

Однако, если семантические контракты двух интерфейсных методов противоречат друг другу, вы в значительной степени проиграли; тогда вы не сможете реализовать оба интерфейса в одном классе.

2023-10-13 18:57 java