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

Posted

技术标签:

【中文标题】用相同的方法在一个类中实现两个接口。哪个接口方法被覆盖?【英文标题】:Implementing two interfaces in a class with same method. Which interface method is overridden? 【发布时间】:2011-02-17 14:46:26 【问题描述】:

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

例如:

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 又是 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!!!"
    

上面的sn-p编译运行。

请注意,只有一个@Override必需!!!。这是因为Gift.present()Guest.present() 是“@Override-equivalent”(JLS 8.4.2)。

因此,johnny只有present() 的一种实现,不管你如何对待johnny,无论是作为Gift 还是作为Guest,只有一种方法可以调用。


不兼容示例

这是一个例子,其中两个继承的方法不是@Override-equivalent:

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。出于同样的原因,您不能在一种类型中使用 void present()boolean present(),此示例会导致编译错误。


总结

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

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

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

参考文献

JLS 8.4.2 Method Signature JLS 8.4.8 Inheritance, Overriding, and Hiding JLS 8.4.8.3 Requirements in Overriding and Hiding JLS 8.4.8.4 Inheriting Methods with Override-Equivalent Signatures “一个类可以继承多个具有重写等效签名的方法。”

【讨论】:

谢谢 - 这很有帮助。但是,我还有一个关于不兼容的问题,我已将其发布为new question 顺便说一句,随着 Java 8 中 default 方法的支持,这会有所改变。 解决潜在不兼容性的复合类可能是诀窍:),但是,我从来没有遇到过这样的问题,但很明显它可能会发生。 这个article 提供了一个设计模式,可以用来在某种程度上处理需要实现两个碰撞接口的情况,比如FooBar .基本上你让你的类实现其中一个接口,比如Foo,并提供一个Bar asBar() 方法来返回一个实现第二个Bar 接口的内部类。不完美,因为您的课程最终不是“酒吧”,但在某些情况下可能很有用。 我是一名 java 开发人员,但 c# 在这方面确实更聪明:***.com/questions/2371178/…【参考方案2】:

这被标记为与此问题重复https://***.com/questions/24401064/understanding-and-solving-the-diamond-problems-in-java

你需要 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 cmets,您可以解决这个问题。

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 中创建默认值? 便于向接口(特别是集合接口)添加新方法,而不会破坏 60% 的代码库。 @BoratSagdiyev 最大的原因是支持关闭并使其更有用。请参阅 Collection.stream()。看看 List.sort() docs.oracle.com/javase/8/docs/api/java/util/… 他们为所有列表添加了一个方法,无需更改任何具体实现。他们添加了有用的 Collection.removeIf() @TassosBassoukos +1 说你有自己的 List 实现,现在你可以 myList.stream() 它或 myList.sort() 它而不改变你的代码 @PeterLawrey:AB 不会编译,因为它必须覆盖 hi()(以解决歧义)。例如,通过将其实现为A.super.hi() 来选择与A 相同的实现方式。【参考方案3】:

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

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

【讨论】:

它解释了为什么 Java 不允许你扩展多个类 @ArthurRonald,实际上它只是看起来相关。但是,IMO,扩展多个类的类可能会遇到 Diamond 问题(这是最派生类中的重复对象状态),这很可能是 Java 让其用户远离麻烦的原因。另一方面,实现多个类的类永远不会遇到钻石问题,因为接口不向对象提供状态。问题纯粹是由于语法限制 - 无法完全限定函数调用。【参考方案4】:

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

但是,如果两个接口方法的 语义 契约相互矛盾,那你就输了;那么你不能在一个类中实现这两个接口。

【讨论】:

【参考方案5】:

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

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

请看下面的代码:

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

所以编译器出了问题。

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

【讨论】:

【参考方案6】:

尝试以匿名方式实现接口。

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




【讨论】:

【参考方案7】:

好吧,如果它们都相同,那没关系。它使用每个接口方法的一个具体方法来实现它们。

【讨论】:

以上是关于用相同的方法在一个类中实现两个接口。哪个接口方法被覆盖?的主要内容,如果未能解决你的问题,请参考以下文章

当两个方法同名但参数不同时如何在VB.Net中实现一个接口

从具有相同方法名称的多个接口继承

在python中实现类接口的正确方法是啥

PowerDesigner 不同包中实现接口

Java中实现多态的机制

如何在java中实现接口类