假设我有以下结构:
abstract class A {
abstract boolean foo();
}
interface B {
default boolean foo() { return doBlah(); }
}
class C extends A implements B {
//function foo
}
Java现在会抱怨类C
必须从A
实现抽象方法foo。
通过重新定义C
中的函数并简单地调用B.super.foo();
,我可以相对轻松地解决此问题。
但是我不明白为什么接口B
的默认函数不能单独满足这个要求,我希望能更好地理解java的基础机制。
答案 0 :(得分:4)
在程序的当前状态下,如果要覆盖A#foo
中的C.java
(默认方法返回true
并且重写方法返回false
),则打印{{ 1}}会导致C#foo
,完全忽略默认方法。
抽象类中定义的任何抽象方法都需要由扩展抽象类的第一个具体类覆盖。因此,false
必须覆盖C.java
。
不需要覆盖接口内的默认方法。
但是,这两种方法共享相同的签名,这意味着需要覆盖一个签名,而另一个可能被覆盖。
这是极差的设计,不应该使用,因为共享相同签名的方法不能被覆盖。如果您希望被覆盖抽象方法,则只需将抽象方法的名称或默认方法更改为A#foo
以外的其他方法。
foo
如果您希望在某些情况下只覆盖abstract class A {
abstract boolean bar();
}
interface B {
default boolean foo() { return doBlah(); }
}
class C extends A implements B {
@Override
public boolean foo() {
...
}
@Override
public boolean bar() {
...
}
}
,那么您可以完全删除抽象方法,并在A#foo
中保留默认方法,并在B.java
中覆盖它:
C.java
如果无法删除abstract class A {
}
interface B {
default boolean foo() { return doBlah(); }
}
class C extends A implements B {
@Override
public boolean foo() {
...
}
}
,请将默认方法A#foo
重命名为其他内容。
B#foo
答案 1 :(得分:4)
接口中的默认方法用于防止在向接口添加方法时破坏依赖于接口的程序。
在您描述的情况下,不清楚界面中的默认方法(按照设计必须在中添加)实际上是否符合抽象类最初设想的合同。 / p>
在这种情况下,Java更容易抱怨。 以上文字是我对9.4.1.3 of the JLS SE8 spec中段落的解释,我引用: 类似地,当继承具有匹配签名的抽象和默认方法时,我们会产生错误。在这种情况下,可以优先考虑一个或另一个 - 也许我们假设默认方法也为抽象方法提供了合理的实现。但这是有风险的,因为除了巧合的名称和签名之外,我们没有理由相信默认方法与抽象方法的契约一致 - 默认方法在最初开发子接口时可能不存在。在这种情况下,要求用户主动声明默认实现是合适的(通过覆盖声明)更安全。
答案 2 :(得分:2)
Jacob G.的答案激励我提出这个解决方案:
interface Z {
abstract boolean foo();
}
abstract class A implements Z {
}
interface B extends Z {
default boolean foo() { return doBlah(); }
}
class C extends A implements B {
}
这样,类A
的所有子类都需要定义方法foo()
,而不需要每个实现B
的类重新实现它。