处理谐音inheritance方法的规则是什么?

我试图理解Java如何处理在具体类inheritance(抽象或具体)具有来自不同类/接口的相同名称的方法时出现的歧义情况。

我无法找到一般规则,这就是为什么我一劳永逸地决定用一种实用的方法来花一些时间。

我考虑了8种不同的情况

  • 抽象方法
  • 非抽象方法
  • 抽象类
  • 接口

导致这个计划:

+-------------------------+ | INTERFACE | +----------+--------------| | abstract | non-abstract | | method | method | +-----------+--------------+----------+--------------+ | | abstract | | | | ABSTRACT | method | 1a | 2a | | +--------------+----------+--------------+ | CLASS | non-abstract | | | | | method | 3a | 4a | +-----------+--------------+----------+--------------+ | | abstract | | | | | method | 1b | 2b | | INTERFACE +--------------+----------+--------------+ | | non-abstract | | | | | method | 3b | 4b | +-----------+--------------+----------+--------------+ 

在这里,每个案例都得到实施和评论:

 // (1a) // A - abstract method // I - abstract method // // Implementation needed to avoid compilation error: // "The type B1 must implement the inherited abstract method A1.foo()" // abstract class A1{ abstract void foo(); } interface I1{ void foo(); } class B1 extends A1 implements I1{ public void foo(){} } // (2a) // A - abstract method // I - non-abstract method // // Implementation needed to avoid compilation error: // "The type B2 must implement the inherited abstract method A2.foo()" // abstract class A2{ abstract void foo(); } interface I2{ default void foo(){} } class B2 extends A2 implements I2{ public void foo(){} } // (3a) // A - non-abstract method // I - abstract method // // Implementation not needed // abstract class A3{ public void foo(){} } interface I3{ void foo(); } class B3 extends A3 implements I3{ } // (4a) // A - non-abstract method // I - non-abstract method // // Implementation not needed // abstract class A4 { public void foo(){System.out.println("A4");}} interface I4{default void foo(){ System.out.println("I4");} } class B4 extends A4 implements I4{ B4(){foo();} /*prints "A4"*/ } // (1b) // J - abstract method // K - abstract method // // Implementation needed to avoid compilation error: // "The type C1 must implement the inherited abstract method K1.foo()" // interface J1{ void foo(); } interface K1{ void foo(); } class C1 implements J1,K1{ public void foo(){} } // (2b) // J - abstract method // K - non-abstract method // // Implementation needed to avoid compilation error: // "The default method foo() inherited from K2 conflicts with another // method inherited from J2" // interface J2{ void foo(); } interface K2{ default void foo(){} } class C2 implements J2,K2{ public void foo(){} } // (3b) // J - non-abstract method // K - abstract method // // Implementation needed to avoid compilation error: // "The default method foo() inherited from J3 conflicts with another // method inherited from K3" // interface J3{ default void foo(){} } interface K3{ void foo(); } class C3 implements J3,K3{ public void foo(){} } // (4b) // J - non-abstract method // K - non-abstract method // // Implementation needed to avoid compilation error: // "Duplicate default methods named foo with the parameters () and () // are inherited from the types K4 and J4" // interface J4{ default void foo(){} } interface K4{ default void foo(){} } class C4 implements J4,K4{ public void foo(){} } 

无论如何,尽管我已经能够理解我的例子中的大部分案例,但我还是无法推断出任何“一般规则”。

例如,我不明白为什么案例2a3a的工作方式不同,即为什么接受抽象类给出的实现,而接口给出的实现不是。

我的最后一个问题是:是否真的存在“一般规则”? 有什么方法可以预测编译器的行为,而不必记住每一个案例?

编辑

这可能是微不足道的,但我认为对其他人做出考虑可能会有所帮助。

我想你可以通过这个简单的步骤总结所有问题:

给出这样的示例代码

 abstract class A{abstract void foo();} abstract class B extends A {protected void foo(){}} interface I{void foo();} interface J{default void foo(){}} class C extends B implements I,J{} 
  1. 考虑你的C类由它的所有方法和inheritance的方法组成(称之为C *)

    class C* implements I,J{protected void foo(){};}

  2. 针对它实现的接口validationC *(来自接口的每个方法歧义,包括默认方法,必须通过给出实现来解决)。

    3A。 如果C *在此提供有效的实现停止

    (it's not the case because method visibility cannot be reduced from public to protected)

    3B。 否则,C中需要有效的实现

    class C extends B implements I,J{public void foo(){}}

JLS声明( §9.4.1.3第7段):

当inheritance具有匹配签名的抽象和默认方法时,我们会产生错误。

然后他们继续解释他们为什么选择这个:

在这种情况下,可以优先考虑一个或另一个 – 也许我们假设默认方法也为抽象方法提供了合理的实现。 但这是有风险的,因为除了巧合的名称和签名之外, 我们没有理由相信默认方法与抽象方法的契约一致 – 默认方法在最初开发子接口时可能不存在。 在这种情况下,要求用户主动声明默认实现是合适的(通过覆盖声明)更安全。

一般规则是类中的定义优先于接口中的defender方法。 如果某个方法在类中声明为abstract,则会否决该接口中是否存在默认实现。

引入Defender方法作为权宜之计,允许接口API的增长而不会破坏向后兼容性。 语义必须是这样的,防御者方法不会妨碍任何现有的语言规则。