java - 为什么 Java 8 接口(interface)方法中不允许使用 "final"?

标签 java language-design java-8 default-method jsr335

Java 8 最有用的功能之一是接口(interface)上的新 default 方法。引入它们本质上有两个原因(可能还有其他原因):

从 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);
}

现在,defaultfinal显然是矛盾的关键字,但default关键字本身would not have been strictly required ,所以我假设这种矛盾是故意的,以反射(reflect)“带有主体的类方法”(只是方法)和“带有主体的接口(interface)方法”(默认方法),即我尚未理解的差异。

在某些时候,接口(interface)方法对 staticfinal 等修饰符的支持尚未得到充分探索,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 { 
}

在这里,一切都很好; CA 继承 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/

相关文章:

java - 在 Visual Studio 2015 上运行 xamarin 时出现问题 "Could not locate Java 6 or 7 SDK"

c# - 被拳击迷惑。将 -1 转换为 Int64 会抛出 InvalidCastException

php - PHP 中 "associate"和 "indexed"数组之间的实际区别是什么?

java - lambda表达式只适用于函数式接口(interface)吗?

java - 默认为空 lambda 比检查可能为 null 的 lambda 好还是坏?

java - 使用 Stream 避免 NoSuchElementException

java - 谷歌登录 fragment 中出现错误,W/IInputConnectionWrapper(20367) : showStatusIcon on inactive InputConnection

java - 如何使用 Java 在 azure blob 中私下上传文件后获取 SAS URL

java - 同步与 ReadWriteLock 性能

exception - 封装和异常