【问题标题】:What are the rules to handle homonym inherited methods?处理同音继承方法的规则是什么?
【发布时间】:2015-07-24 18:23:09
【问题描述】:

我试图了解 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      |
+-----------+--------------+----------+--------------+

这里每个案例都被实施和评论:

// (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 由它的所有方法和继承的方法组成(称之为 C*)

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

  2. 根据 C* 实现的接口验证 C*(来自接口的每个方法歧义,包括默认方法,都必须在 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(){}}

【问题讨论】:

  • 请格式化您的帖子。我在使用您提供的基于文本的图表时遇到问题
  • 我可以看到所有的格式都正确,你指的是什么?
  • @VinceEmigh 如果您使用的是移动设备,他们有时会为代码块使用可变宽度字体。
  • 我在打电话。文本没有对齐,我真的无法匹配任何东西;该图表的格式不适合移动设备。以后,我会尝试使用图片来确保每个人看到的结果都一样
  • 在我的安卓手机上,使用 SO 应用,一切正常,对不起!

标签: java oop interface overriding abstract


【解决方案1】:

JLS 声明(§9.4.1.3 第 7 段):

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

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

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

【讨论】:

    【解决方案2】:

    一般规则是类中的定义优先于接口中的防御者方法。如果一个方法在一个类中被声明为抽象的,那会否决接口中默认实现的存在。

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

    【讨论】:

    • 您能提供任何参考的链接吗?课程优先这一事实也是我的直观答案,我想更深入地了解这一点
    猜你喜欢
    • 2013-08-18
    • 2012-04-11
    • 2013-12-29
    • 1970-01-01
    • 2015-09-20
    • 2022-07-29
    • 1970-01-01
    • 1970-01-01
    • 2017-12-26
    相关资源
    最近更新 更多