Реализация двух интерфейсов в классе с одним и тем же методом. Какой метод интерфейса переопределен?
Два интерфейса с одинаковыми именами методов и сигнатурами. Но реализованы одним классом, тогда как компилятор определит, какой метод для какого интерфейса?
Пример:
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
-эквивалентные, это один и тот же метод.
Устранение потенциальных несовместимостей может быть сложной задачей, но это совсем другая проблема.
Ссылки
- Сигнатура метода JLS 8.4.2
- JLS 8.4.8 Наследование, переопределение и скрытие
- Требования JLS 8.4.8.3 к переопределению и скрытию
- JLS 8.4.8.4 Наследование методов с сигнатурами, эквивалентными переопределению
- "Класс может наследовать несколько методов с сигнатурами, эквивалентными переопределению".
Ответ 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
Идентифицировать нечего. Интерфейсы запрещают только имя метода и сигнатуру. Если оба интерфейса имеют метод с точно таким же именем и сигнатурой, реализующий класс может реализовать оба метода интерфейса одним конкретным методом.
Однако, если семантические контракты двух интерфейсных методов противоречат друг другу, вы в значительной степени проиграли; тогда вы не сможете реализовать оба интерфейса в одном классе.