处理谐音继承方法有哪些规则?

Lui*_*ese 5 java oop overriding interface abstract

我试图理解Java如何处理当具体类从不同的类/接口继承(抽象或具体)具有相同名称的方法时出现的歧义情况.

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

我考虑了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      |
+-----------+--------------+----------+--------------+
Run Code Online (Sandbox Code Playgroud)

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

// (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(){}     }
Run Code Online (Sandbox Code Playgroud)

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

例如,我不明白为什么案例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{}
Run Code Online (Sandbox Code Playgroud)
  1. 考虑你的C类由它的所有方法和继承的方法组成(称之为C*)

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

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

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

Vin*_*igh 5

JLS 状态 ( \xc2\xa79.4.1.3第 7 段):

\n\n
\n

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

\n
\n\n

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

\n\n
\n

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

\n
\n