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

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

Пример:

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() (как, например, guest присутствует и не отсутствует).

Presentable johnny является как Gift, так и 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 Johnny!!!");
            }
        };
        johnny.present();                     // "Heeeereee Johnny!!!"

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

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

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

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

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

Таким образом, johnny имеет только одну реализацию present(), и не имеет значения, как вы относитесь к johnny, будь то Gift или как 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() и boolean present() в одном типе, этот пример приводит к ошибке компиляции.


Резюме

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

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

Разрешение потенциальных несовместимостей может быть сложной задачей, но это еще одна проблема.

Ссылки

Ответ 2

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

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

Ответ 3

Это было отмечено как дубликат этого вопроса fooobar.com/questions/4605/...

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

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.

Ответ 4

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

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

Ответ 5

Попробуйте реализовать интерфейс как анонимный.

public class MyClass extends MySuperClass implements MyInterface{

MyInterface myInterface = new MyInterface(){

/* Overrided method from interface */
@override
public void method1(){

}

};

/* Overrided method from superclass*/
@override
public void method1(){

}

}

Ответ 6

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

Но когда два интерфейса имеют метод с тем же именем, но с другим типом возвращаемого значения, и вы реализуете два метода в конкретном классе:

Пожалуйста, смотрите ниже код:

public interface InterfaceA {
  public void print();
}


public interface InterfaceB {
  public int print();
}

public class ClassAB implements InterfaceA, InterfaceB {
  public void print()
  {
    System.out.println("Inside InterfaceA");
  }
  public int print()
  {
    System.out.println("Inside InterfaceB");
    return 5;
  }
}

когда компилятор получает метод "public void print()", он сначала смотрит в InterfaceA, и он получает его. Но все же он дает ошибку времени компиляции, возвращаемый тип несовместим с методом InterfaceB.

Таким образом, он компилируется для компилятора.

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

Ответ 7

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