具有相同方法名称和签名的两个接口。但是由单个类实现,那么编译器将如何识别哪个方法用于哪个接口?
前任:
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;
}
}
如果一个类型实现了两个接口,并且每个 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
继承成员必须遵守成员声明的一般规则。这里我们用不兼容的返回类型定义了 Gift
和 Guest
:一个 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 继承具有覆盖等效签名的方法“一个类可以继承具有覆盖等效签名的多个方法。”
“一个类可以继承多个具有重写等效签名的方法。”
这被标记为与此问题重复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.
hi()
(以解决歧义)。例如,通过将其实现为 A.super.hi()
来选择以与 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 的方法不兼容。
所以编译器就乱套了。
这样,您将无法实现两个具有相同名称但返回类型不同的方法的接口。
尝试将接口实现为匿名。
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(){
}
}
也可以采取以下两种方法来实现重复的方法并避免歧义 -
方法一:
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
default
方法的支持,这发生了一些变化。Foo
和Bar
)的情况。基本上,您让您的类实现其中一个接口,例如Foo
,并提供一个Bar asBar()
方法来返回实现第二个Bar
接口的内部类。不完美,因为您的课程最终不是“酒吧”,但在某些情况下它可能很有用。