ChatGPT解决这个技术问题 Extra ChatGPT

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

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

前任:

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

p
polygenelubricants

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

上面的代码片段编译并运行。

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

概括

您可以继承与 @Override 等效的方法,但要遵守方法覆盖和隐藏的常规要求。由于它们 ARE @Override 等效,因此实际上只有一种方法可以实现,因此无需区分/选择。

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

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

参考

JLS 8.4.2 方法签名

JLS 8.4.8 继承、覆盖和隐藏

JLS 8.4.8.3 覆盖和隐藏的要求

JLS 8.4.8.4 继承具有覆盖等效签名的方法“一个类可以继承具有覆盖等效签名的多个方法。”

“一个类可以继承多个具有重写等效签名的方法。”


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

这被标记为与此问题重复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 评论的那样,您可以解决这个问题。

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 相同的方式实现它。
A
Ash

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

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


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

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

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


P
Paul Whelan

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


C
Constantin

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

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

请看下面的代码:

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

所以编译器就乱套了。

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


d
dcanh121

尝试将接口实现为匿名。

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

}

}

P
Payel Senapati

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

方法一:

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

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅