问题描述
我试图了解 Java 如何处理当具体类从不同类/接口继承(抽象或具体)具有相同名称的方法时出现的歧义情况.
I'm trying to understand how Java handles cases of ambiguity that come out when a concrete class inherits (abstract or concrete) methods having the same name from different classes/interfaces.
我一直无法找到一般规则,这就是为什么我决定花一些时间通过使用实际方法来解决这个问题.
I've not been able to find a general rule, this is why I decided, once for all, to spend some time on this by using a practical approach.
我考虑了 8 种不同的情况,结合起来
I considered 8 different cases, combining
- 抽象方法
- 非抽象方法
- 抽象类
- 界面
导致这个方案:
+-------------------------+
| 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 |
+-----------+--------------+----------+--------------+
这里每个案例都被实施和评论:
And here every case is implemented and commented:
// (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(){} }
无论如何,尽管我能够理解示例中的大多数情况,但我还无法推断出任何一般规则".
Done that, anyway, albeit I'm able to understand the majority of the cases in my example, I've not been able to infer any "general rule" yet.
例如,我不明白为什么情况 2a 和 3a 的工作方式不同,即为什么抽象类给出的实现被接受,而抽象类给出的实现被接受界面不是.
For instance, I don't understand why cases 2a and 3a work differently, i.e. why an implementation given by the abstract class is accepted while an implementation given by the interface is not.
我的最后一个问题是:真的存在任何一般规则"吗?有没有什么方法可以预测编译器的行为而不必记住每个案例?
My final question is: does any "genaral rule" really exist? Is there any way I can predict how the compiler will behave without having to memorize every single case?
这可能是微不足道的,但我认为这对其他人放下我的考虑很有用.
It could be trivial, but I think it can be useful to someone else putting down my considerations.
我认为您可以将所有问题总结为以下简单步骤:
I think you can summarize all the question to this simple steps:
给出这样的示例代码
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{}
考虑你的类 C 由它的所有方法和继承的方法组成(称之为 C*)
Consider your class C made up of all its methods and inherited ones (call it C*)
class C* 实现 I,J{protected void foo(){};}
针对它实现的接口验证 C*(来自接口的每个方法歧义,包括默认方法,都必须在 C 中通过提供实现来解决).
Validate C* against interfaces it implements (every method ambiguity coming from interfaces, including default methods, must be resolved in C by giving an implementation).
3a.如果 C* 给出了一个有效的实现,请在此处停止
3a. If C* gives a valid implementation stop here
(情况并非如此,因为方法的可见性无法从公共降低到受保护)
3b.否则在 C 中需要一个有效的实现
3b. Otherwise a valid implementation is needed in C
class C extends B 实现 I,J{public void foo(){}}
推荐答案
JLS 声明 (§9.4.1.3 第 7) 段:
The JLS states (§9.4.1.3 paragraph 7):
当继承了具有匹配签名的抽象方法和默认方法时,我们会产生错误.
然后他们继续解释他们为什么选择这个:
They then go on to explain why they chose this:
在这种情况下,可以优先考虑其中一个——也许我们会假设默认方法也为抽象方法提供了合理的实现.但这是有风险的,因为除了巧合的名称和签名之外,我们没有理由相信默认方法的行为与抽象方法的契约一致 - 当子接口出现时,默认方法甚至可能不存在最初是开发的.在这种情况下,要求用户主动断言默认实现是合适的(通过覆盖声明)更安全.
这篇关于处理同音继承方法的规则是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!