用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?


235

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

例如:

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

Answers:


337

如果一个类型实现两个接口,并且每个接口interface定义一个具有相同签名的方法,则实际上只有一个方法,并且它们是不可区分的。如果说这两个方法的返回类型冲突,那么将是编译错误。这是继承,方法覆盖,隐藏和声明的一般规则,并且不仅适用于两个继承的interface方法之间的可能冲突,还适用于an interface和super class方法之间的冲突,甚至仅适用于泛型类型擦除引起的冲突。


相容性范例

在下面的示例中,您有一个interface Gift具有present()方法(例如,赠送礼物)的和interface Guest,还具有一种present()方法(例如,客人在场并且不在场)。

Presentable johnny 都是 GiftGuest

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必须继承成员必须遵守成员声明的一般规则。下面我们就GiftGuest定义present()不兼容的返回类型:一个void其他的boolean。由于不能同时使用an void present()boolean present()in 的同一原因,此示例导致编译错误。


摘要

您可以继承@Override-equivalent的方法,但要遵循方法覆盖和隐藏的通常要求。由于它们 @Override等效的,因此实际上只有一种方法可以实现,因此没有区别/选择的地方。

编译器不必确定哪个方法用于哪个接口,因为一旦确定它们是 @Override它们等效,它们就是相同的方法。

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

参考资料


谢谢-这很有帮助。但是,我还有一个关于不兼容性的问题,我已将其发布为新问题
maiidment

2
BTW这改变了一点点的支持下,default方法的Java 8
彼得Lawrey

解决潜在的不兼容问题的复合类可能是诀窍:),但是,我从来没有遇到过这样的问题,而且仍然很可能会发生。
Aquarius Power

1
文章提供可用于设计模式有所用,你需要实现两个接口碰撞处理的情况,说FooBar。基本上,您的类实现了一个接口(例如)Foo,并提供了一种Bar asBar()方法来返回实现第二个Bar接口的内部类。由于您的课程最终不是“酒吧”,因此并不完美,但是在某些情况下它可能会很有用。
Javaru

1
我是一个Java开发者,但是C#确实是更聪明的是:stackoverflow.com/questions/2371178/...
阿米尔Ziarati

25

这被标记为该问题的重复/programming/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.

哇。这对我来说是新的。为什么他们必须在Java 8中创建默认值?
Erran Morad 2014年

1
为了便于向接口(特别是集合接口)添加新方法而不会破坏60%的代码库。
Tassos Bassoukos

@BoratSagdiyev最大的原因是支持closes,并使其更有用。请参见Collection.stream()。看看List.sort()docs.oracle.com/javase/8/docs/api/java/util/… 他们为所有Lists添加了一种方法,而无需更改任何特定的实现。他们添加了有用的Collection.removeIf()
Peter Lawrey 2014年

@TassosBassoukos +1说您有自己的List实现,现在您可以在不更改代码的情况下使用myList.stream()或myList.sort()了
Peter Lawrey 2014年

3
@PeterLawrey:AB不会编译,因为它必须重写hi()(以解决歧义)。例如,通过实现它作为A.super.hi()选择来实现它的方式一样A.
JB Nizet

20

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

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


2
它解释了为什么Java不允许您扩展多个类
Arthur Ronald

1
@ArthurRonald,实际上它看起来很相关。但是,扩展了多个类的IMO 类可能会遇到“钻石问题”(Diamond Problem)(在最衍生的类中是重复的对象状态),这很可能就是Java使用户摆脱麻烦的原因。另一方面,实现多个类的类永远不会因为“接口”不向对象提供状态而遇到“钻石问题”。问题完全是由于语法限制-无法完全限定函数调用。
uvsmtid '16

13

没有什么可识别的。接口仅禁止使用方法名称和签名。如果两个接口都具有名称和签名完全相同的方法,则实现类可以使用单个具体方法来实现这两个接口方法。

但是,如果两个接口方法的语义协定相互矛盾,那么您就迷失了很多。您不能在一个类中同时实现两个接口。


4

尝试将接口实现为匿名。

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

}

}

4

就像在接口中一样,我们只是在声明方法,实现这两个接口的具体类都知道只有一个方法(正如您所描述的,两个方法在返回类型中具有相同的名称)。因此应该没有问题。您将能够在具体的类中定义该方法。

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

请看下面的代码:

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

因此,对于编译器而言,这很麻烦。

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


3

好吧,如果它们都相同,那就没关系了。每个接口方法都使用单个具体方法来实现这两种方法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.