Java 8 最有用的功能之一是接口(interface)上的新 default
方法。引入它们本质上有两个原因(可能还有其他原因):
- 提供实际的默认实现。示例:
Iterator.remove()
- 允许 JDK API 的演变。示例:
Iterable.forEach()
从 API 设计者的角度来看,我希望能够在接口(interface)方法上使用其他修饰符,例如最终
。这在添加便捷方法时非常有用,可以防止实现类时的“意外”覆盖:
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
显然是矛盾的关键字,但default关键字本身would not have been strictly required ,所以我假设这种矛盾是故意的,以反射(reflect)“带有主体的类方法”(只是方法)和“带有主体的接口(interface)方法”(默认方法),即我尚未理解的差异。
在某些时候,接口(interface)方法对 static
和 final
等修饰符的支持尚未得到充分探索,citing Brian Goetz :
The other part is how far we're going to go to support class-building tools in interfaces, such as final methods, private methods, protected methods, static methods, etc. The answer is: we don't know yet
自 2011 年底以来,显然添加了对接口(interface)中静态
方法的支持。显然,这为 JDK 库本身增加了很多值(value),例如 Comparator.comparing()
.
问题:
final
(以及static final
)从未出现在 Java 8 接口(interface)中的原因是什么?
最佳答案
这个问题在某种程度上与What is the reason why “synchronized” is not allowed in Java 8 interface methods?相关。
了解默认方法的关键是,主要设计目标是接口(interface)进化,而不是“将接口(interface)变成(平庸的)特征”。虽然两者之间存在一些重叠,并且我们试图在不妨碍前者的情况下适应后者,但从这个角度来看这些问题是最好理解的。 (还要注意,无论意图是什么,类方法都将与接口(interface)方法不同,因为接口(interface)方法可以被多重继承。)
默认方法的基本思想是:它是一个具有默认实现的接口(interface)方法,派生类可以提供更具体的实现。而且由于设计中心是接口(interface)演化,因此一个关键的设计目标是能够在事后以源代码兼容和二进制兼容的方式将默认方法添加到接口(interface)中。
对于“为什么不是最终默认方法”的过于简单的答案是,那么主体将不仅仅是默认实现,它将是唯一的实现。虽然这个答案有点过于简单,但它给我们提供了一个线索,即问题已经朝着有问题的方向发展。
最终接口(interface)方法值得怀疑的另一个原因是它们给实现者带来了不可能的问题。例如,假设您有:
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
必须重写它(如果想要保留相同的行为,可以选择委托(delegate)给 A.super.foo()
。)但是如果 B
会怎样? > 已将其默认为final
,而A
不在C
作者的控制之下?现在C
已经彻底崩溃了;如果不重写 foo()
,它就无法编译,但如果它在 B
中是最终的,它就无法重写 foo()
。
这只是一个例子,但要点是,方法的最终性实际上是一种工具,它在单继承类(通常将状态与行为耦合)的世界中比仅贡献行为和行为的接口(interface)更有意义。可以多重遗传。很难推理“哪些其他接口(interface)可能会混合到最终的实现者中”,并且允许接口(interface)方法是最终的可能会导致这些问题(并且它们不会在编写接口(interface)的人身上爆炸,而是在接口(interface)方法上爆炸)尝试实现它的可怜的用户。)
禁止使用它们的另一个原因是它们的含义与您所认为的不同。仅当类(或其父类(super class))不提供方法的声明(具体或抽象)时,才会考虑默认实现。如果默认方法是final的,但父类(super class)已经实现了该方法,则默认方法将被忽略,这可能不是默认作者在声明它为final时所期望的。 (这种继承行为反射(reflect)了默认方法的设计中心——接口(interface)演化。应该可以向已有实现的现有接口(interface)中添加默认方法(或现有接口(interface)方法的默认实现),而不需要更改实现该接口(interface)的现有类的行为,保证在添加默认方法之前已经工作的类在存在默认方法的情况下将以相同的方式工作。)
关于java - 为什么 Java 8 接口(interface)方法中不允许使用 "final"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57410256/