【问题标题】:Implementing two interfaces in a class with same method. Which interface method is overridden?使用相同的方法在一个类中实现两个接口。重写了哪个接口方法?
【发布时间】:2022-12-03 18:55:39
【问题描述】:

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

前任:

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

【问题讨论】:

    标签: java interface overriding


    【解决方案1】:

    如果一个类型实现了两个接口,并且每个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!!!"
        }
    }
    

    以上sn -p编译运行。

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


    概括

    您可以继承 @Override 等效的方法,但要满足方法覆盖和隐藏的通常要求。自从他们@Override-等效,实际上只有一种方法可以实现,因此没有什么可以区分/选择的。

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

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

    参考

    【讨论】:

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

    这被标记为这个问题的重复https://stackoverflow.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,类延伸不止一个类会遇到钻石问题(这是最派生类中的重复对象状态),这很可能是 Java 让用户远离这些麻烦的原因。另一方面,类 which工具不止一个班级可以绝不遇到钻石问题只是因为接口不向对象提供状态。问题纯粹是由于语法限制——无法完全限定函数调用。
    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案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】:

          尝试将接口实现为匿名。

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

          【讨论】:

            【解决方案8】:

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

            方法一:

            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
            

            【讨论】:

              猜你喜欢
              • 2014-08-15
              • 2013-12-01
              • 2019-01-29
              • 1970-01-01
              • 2020-12-09
              • 1970-01-01
              相关资源
              最近更新 更多