Реализация двух интерфейсов в классе с одним и тем же методом. Какой интерфейсный метод переопределен? - PullRequest
204 голосов
/ 10 мая 2010

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

Ex:

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;
  }
}   

Ответы [ 7 ]

294 голосов
/ 14 мая 2010

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


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

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

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'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 или как 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, определяющие present() с несовместимыми типами возврата: один void другой boolean. По той же причине, по которой вы не можете использовать void present() и boolean present() в одном типе, этот пример приводит к ошибке компиляции.


Резюме

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

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

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

Ссылки

20 голосов
/ 10 мая 2010

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

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

19 голосов
/ 25 июня 2014

Это был отмечен как дубликат к этому вопросу https://stackoverflow.com/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.
12 голосов
/ 10 мая 2010

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

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

4 голосов
/ 07 мая 2014

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

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

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

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.

Так что компилятор не работает.

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

4 голосов
/ 07 октября 2011

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

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(){

}

}
3 голосов
/ 10 мая 2010

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

...