Java 8 最有用的特性之一是接口上的新 default
方法。引入它们主要有两个原因(可能还有其他原因):
- 提供实际的默认实现。示例:
Iterator.remove()
- 允许 JDK API 进化。示例:
Iterable.forEach()
从 API 设计者的角度来看,我希望能够在接口方法上使用其他修饰符,例如 final
。这在添加便利方法时很有用,可以防止在实现类时出现“意外”覆盖:
interface Sender {
// Convenience method to send an empty message
default final void send() {
send(null);
}
// Implementations should only implement this method
void send(String message);
}
如果 Sender
是一个类,以上已经是常见的做法:
abstract class Sender {
// Convenience method to send an empty message
final void send() {
send(null);
}
// Implementations should only implement this method
abstract void send(String message);
}
现在, default
和 final
显然是矛盾的关键字,但默认关键字本身 并不是严格要求 的,所以我假设这种矛盾是故意的,以反映细微的差异在 “带主体的类方法” (只是方法)和 “带主体的接口方法” (默认方法)之间,即我尚未理解的差异。
引用 Brian Goetz 的话,在某些时候,接口方法上对修饰符的支持,如 static
和 final
尚未得到充分探索:
另一部分是我们将在多大程度上支持接口中的类构建工具,例如最终方法、私有方法、受保护方法、静态方法等。答案是:我们还不知道
从 2011 年底的那个时候开始,很明显,接口中添加了对 static
方法的支持。显然,这为 JDK 库本身增加了很多价值,例如 Comparator.comparing()
。
问题:
是什么原因 final
(以及 static final
)从未进入Java 8接口?
原文由 Lukas Eder 发布,翻译遵循 CC BY-SA 4.0 许可协议
这个问题在某种程度上与 Java 8接口方法中不允许“同步”的原因是什么有关?
了解默认方法的关键是主要设计目标是 _接口进化_,而不是“将接口变成(平庸的)特征”。虽然两者之间有一些重叠,并且我们试图在不妨碍前者的情况下适应后者,但从这个角度来看这些问题最好理解。 (还请注意,无论意图如何,类方法 都 将不同于接口方法,因为接口方法可以被多重继承。)
默认方法的基本思想是:它是一个具有默认实现的接口方法,派生类可以提供更具体的实现。并且因为设计中心是接口演进,所以一个关键的设计目标是能够 在事后 以源代码兼容和二进制兼容的方式将默认方法添加到接口中。
对“为什么不是 final 默认方法”的过于简单的回答是,那么 body 将不仅仅是默认实现,它将是唯一的实现。虽然这个答案有点太简单了,但它给了我们一个线索,即问题已经朝着有问题的方向发展了。
最终接口方法有问题的另一个原因是它们给实现者带来了不可能的问题。例如,假设您有:
在这里,一切都很好;
C
继承foo()
来自A
。现在假设B
更改为具有foo
方法,具有默认值:现在,当我们去重新编译
C
时,编译器会告诉我们它不知道要为foo()
继承什么行为,所以C
has override-d它(并且可以选择委托给A.super.foo()
如果它想保留相同的行为。)但是如果B
A
设为默认值final
---
不受C
作者的控制?现在C
已无法挽回地损坏;它不能在不覆盖的情况下编译foo()
,但它不能覆盖foo()
如果它是最终的B
这只是一个例子,但要点是,方法的终结性实际上是一种工具,在单继承类(通常将状态与行为耦合)的世界中比对仅提供行为并且可以乘以的接口更有意义遗传。很难推断“哪些其他接口可能会混合到最终的实现者中”,并且允许接口方法是最终的可能会导致这些问题(并且它们不会在编写接口的人身上爆炸,而是在试图实施它的可怜的用户。)
不允许使用它们的另一个原因是它们的意思与您认为的不同。仅当类(或其超类)不提供方法的声明(具体或抽象)时才考虑默认实现。如果默认方法是最终的,但超类已经实现了该方法,则默认值将被忽略,这可能不是默认作者在声明它时所期望的最终方法。 (这种继承行为反映了默认方法的设计中心——接口演化。应该可以在现有的已经有实现的接口上添加一个默认方法(或者对现有接口方法的默认实现),而不用改变实现接口的现有类的行为,保证在添加默认方法之前已经工作的类将在存在默认方法的情况下以相同的方式工作。)