两个具有相同方法名称和签名的接口。但是由单个类实现那么编译器将如何识别哪个方法是为哪个接口?

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

当前回答

这被标记为这个问题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.

其他回答

如果它们都是一样的,那就无所谓了。它通过每个接口方法一个具体的方法来实现这两种方法。

在接口中,我们只是声明方法,实现这两个接口的具体类理解的是只有一个方法(正如你所描述的在返回类型中有相同的名称)。所以应该不会有什么问题。您将能够在具体类中定义该方法。

但是当两个接口有相同名称但不同返回类型的方法时,你在具体类中实现了两个方法:

请看下面的代码:

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的方法不兼容。

所以编译器就乱套了。

通过这种方式,您将无法实现两个具有相同名称但返回类型不同的方法的接口。

If a type implements two interfaces, and each interface define a method that has identical signature, then in effect there is only one method, and they are not distinguishable. If, say, the two methods have conflicting return types, then it will be a compilation error. This is the general rule of inheritance, method overriding, hiding, and declarations, and applies also to possible conflicts not only between 2 inherited interface methods, but also an interface and a super class method, or even just conflicts due to type erasure of generics.


兼容性的例子

下面是一个例子,其中有一个接口Gift,它有一个present()方法(在这里,赠送礼物),还有一个接口Guest,它也有一个present()方法(在这里,客人出现而不是缺席)。

得体的强尼既是礼物,也是客人。

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

这进一步重申了从接口继承成员必须遵守成员声明的一般规则。这里Gift和Guest定义的present()返回类型不兼容:一个为void,另一个为boolean。由于同样的原因,你不能在一个类型中使用void present()和boolean present(),这个例子会导致编译错误。


总结

您可以继承@ override等效的方法,这取决于方法重写和隐藏的通常要求。由于它们是@ override等效的,实际上只有一个方法可以实现,因此没有什么可以区分/选择的。

编译器不必识别哪个方法对应哪个接口,因为一旦确定它们是@ override等效的,它们就是相同的方法。

解决潜在的不兼容性可能是一项棘手的任务,但这完全是另一个问题。

参考文献

JLS 8.4.2方法签名 JLS 8.4.8继承、重写和隐藏 JLS 8.4.8.3覆盖和隐藏的要求 JLS 8.4.8.4继承具有重写等效签名的方法 一个类可以继承多个具有重写等效签名的方法。

就编译器而言,这两个方法是相同的。两者都将有一个实现。

如果这两个方法实际上是相同的,这就不是问题,因为它们应该有相同的实现。如果它们在合同上是不同的(根据每个接口的文档),那么您就有麻烦了。

还可以采用以下两种方法来实现重复的方法并避免歧义-

方法1:

App.java -

public class App {
    public static void main(String[] args) {
        TestInterface1 testInterface1 = new TestInterface1();
        TestInterface2 testInterface2 = new TestInterface2();
        testInterface1.draw();
        testInterface2.draw();
    }
}

TestInterface1.java -

public class TestInterface1 implements Circle {
    
}

TestInterface2.java -

public class TestInterface2 implements Rectangle {
    
}

Circle.java -

public interface Circle extends Drawable {
    @Override
    default void draw() {
        System.out.println("Drawing circle");
    }
}

Rectangle.java -

public interface Rectangle extends Drawable {
    @Override
    default void draw() {
        System.out.println("Drawing rectangle");
    }
}

Drawable.java -

public interface Drawable {
    default void draw() {
        System.out.println("Drawing");
    }
}

输出-

Drawing circle
Drawing rectangle

方法2:

App.java -

public class App {
    public static void main(String[] args) {
        
        Circle circle = new Circle() {
                
        };
        Rectangle rectangle = new Rectangle() {
                
        };

        circle.draw();
        rectangle.draw();
    }
}

Circle.java -

public interface Circle extends Drawable {
    @Override
    default void draw() {
        System.out.println("Drawing circle");
    }
}

Rectangle.java -

public interface Rectangle extends Drawable {
    @Override
    default void draw() {
        System.out.println("Drawing rectangle");
    }
}

Drawable.java -

public interface Drawable {
    default void draw() {
        System.out.println("Drawing");
    }
}

输出-

Drawing circle
Drawing rectangle