理解默认方法的关键是主要设计目标是界面演变,而不是"turn interfaces into (mediocre) traits" . 虽然有可能妨碍前者,但从这个角度来看,这些问题最能被理解 . (另请注意,由于接口方法可以多次继承,因此无论意图如何,类方法都将与接口方法不同 . )
默认方法的基本思想是:它是具有默认实现的接口方法,派生类可以提供更具体的实现 . 而且由于设计中心是接口演化,因此默认方法能够以源兼容和二进制兼容的方式添加到接口之后是一个关键的设计目标 .
对“为什么不是最终默认方法”的过于简单的回答是,然后主体不仅仅是默认实现,它将是唯一的实现 . 虽然这个答案有点过于简单,但它为我们提供了一个线索,即问题已经朝着可疑的方向发展 .
最终接口方法可疑的另一个原因是它们为实现者创建了不可能的问题 . 例如,假设您有:
interface A {
default void foo() { ... }
}
interface B {
}
class C implements A, B {
}
一切都很好; C 从 A 继承 foo() . 现在假设 B 被更改为具有 foo 方法,具有默认值:
interface B {
default void foo() { ... }
}
现在,当我们重新编译 C 时,编译器会告诉我们它不知道要为 foo() 继承什么行为,所以 C 必须覆盖它(并且如果它想要保留相同的行为,可以选择委托给 A.super.foo() . )但是如果 B 已经使其默认 final ,并且 A 不受 C 作者的控制,该怎么办?现在 C 已经无可挽回地破裂了;如果没有覆盖 foo() ,它就无法编译,但如果它在 B 中是最终的,则无法覆盖 foo() .
这只是一个例子,但重点是最终方法实际上是一个在单继承类(通常将状态与行为耦合)的世界中更有意义的工具,而不是仅仅贡献行为并且可以多次继承的接口 . 很难推断“其他接口可能会混入最终的实现者”,并且允许接口方法最终可能会导致这些问题(并且它们不会在编写接口的人身上爆炸,而是在试图实现它的穷人 . )
不允许他们的另一个原因是他们不会意味着你认为他们的意思 . 仅当类(或其超类)不提供方法的声明(具体或抽象)时,才考虑默认实现 . 如果默认方法是final,但是超类已经实现了该方法,则默认值将被忽略,这可能不是默认作者在声明最终时所期望的 . (这种继承行为反映了默认方法的设计中心 - 接口演化 . 应该可以将默认方法(或现有接口方法的默认实现)添加到已经具有实现的现有接口,而无需更改现有类的行为实现接口,保证在添加默认方法之前已经工作的类在默认方法存在时将以相同的方式工作 . )