处理同名继承方法的规则是什么?
What are the rules to handle homonym inherited methods?
我正在尝试了解 Java 如何处理具体 class 从不同 classes/interfaces 继承具有相同名称的(抽象或具体)方法时出现的歧义情况.
我一直没能找到一般规则,这就是为什么我决定一劳永逸地花一些时间用实用的方法解决这个问题。
我考虑了 8 种不同的情况,结合
- 抽象方法
- 非抽象方法
- 摘要classes
- 接口
导致此方案:
+-------------------------+
| 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(){} }
完成了,无论如何,尽管我能够理解示例中的大多数情况,但我还无法推断出任何 "general rule"。
例如,我不明白为什么案例 2a 和 3a 的工作方式不同,即为什么抽象给出的实现 class 被接受,而接口给出的实现不被接受。
我的最后一个问题是:"genaral rule"真的存在吗?有什么方法可以预测编译器的行为而不必记住每个案例?
编辑
这可能是微不足道的,但我认为它对其他人放下我的考虑很有用。
我想你可以把所有的问题总结成这个简单的步骤:
给出这样的示例代码
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{}
考虑一下你的 class C 由它的所有方法和继承的方法组成(称之为 C*)
class C* implements I,J{protected void foo(){};}
根据其实现的接口验证 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(){}}
一般规则是 class 中的定义优先于接口中的防御方法。如果一个方法在 class 中被声明为抽象的,那将否决接口中默认实现的存在。
Defender 方法是作为一种权宜之计而引入的,目的是在不破坏向后兼容性的情况下允许接口 API 的增长。语义必须使得防御者方法不会妨碍任何现有的语言规则。
JLS 声明(§9.4.1.3 第 7 段):
When an abstract and a default method with matching signatures are inherited, we produce an error.
然后他们继续解释他们为什么选择这个:
In this case, it would be possible to give priority to one or the other - perhaps we would assume that the default method provides a reasonable implementation for the abstract method, too. But this is risky, since other than the coincidental name and signature, we have no reason to believe that the default method behaves consistently with the abstract method's contract - the default method may not have even existed when the subinterface was originally developed. It is safer in this situation to ask the user to actively assert that the default implementation is appropriate (via an overriding declaration).
我正在尝试了解 Java 如何处理具体 class 从不同 classes/interfaces 继承具有相同名称的(抽象或具体)方法时出现的歧义情况.
我一直没能找到一般规则,这就是为什么我决定一劳永逸地花一些时间用实用的方法解决这个问题。
我考虑了 8 种不同的情况,结合
- 抽象方法
- 非抽象方法
- 摘要classes
- 接口
导致此方案:
+-------------------------+
| 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(){} }
完成了,无论如何,尽管我能够理解示例中的大多数情况,但我还无法推断出任何 "general rule"。
例如,我不明白为什么案例 2a 和 3a 的工作方式不同,即为什么抽象给出的实现 class 被接受,而接口给出的实现不被接受。
我的最后一个问题是:"genaral rule"真的存在吗?有什么方法可以预测编译器的行为而不必记住每个案例?
编辑
这可能是微不足道的,但我认为它对其他人放下我的考虑很有用。
我想你可以把所有的问题总结成这个简单的步骤:
给出这样的示例代码
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{}
考虑一下你的 class C 由它的所有方法和继承的方法组成(称之为 C*)
class C* implements I,J{protected void foo(){};}
根据其实现的接口验证 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(){}}
一般规则是 class 中的定义优先于接口中的防御方法。如果一个方法在 class 中被声明为抽象的,那将否决接口中默认实现的存在。
Defender 方法是作为一种权宜之计而引入的,目的是在不破坏向后兼容性的情况下允许接口 API 的增长。语义必须使得防御者方法不会妨碍任何现有的语言规则。
JLS 声明(§9.4.1.3 第 7 段):
When an abstract and a default method with matching signatures are inherited, we produce an error.
然后他们继续解释他们为什么选择这个:
In this case, it would be possible to give priority to one or the other - perhaps we would assume that the default method provides a reasonable implementation for the abstract method, too. But this is risky, since other than the coincidental name and signature, we have no reason to believe that the default method behaves consistently with the abstract method's contract - the default method may not have even existed when the subinterface was originally developed. It is safer in this situation to ask the user to actively assert that the default implementation is appropriate (via an overriding declaration).