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)中的 static 方法的支持。显然,这为 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 中是 final,它就不能覆盖 foo()

这只是一个例子,但重点是方法的最终确定性确实是一种工具,它在单继承类(通常将状态与行为耦合)的世界中比对仅贡献行为和行为的接口(interface)更有意义可以多重继承。很难推断“哪些其他接口(interface)可能会混入最终的实现者”,并且允许接口(interface)方法成为 final方法可能会导致这些问题(而且它们不会在编写接口(interface)的人身上爆炸,而是在试图实现它的可怜用户。)

另一个不允许它们的原因是它们的意思不是你认为的那样。仅当类(或其父类(super class))不提供方法的声明(具体或抽象)时才考虑默认实现。如果默认方法是 final方法,但父类(super class)已经实现了该方法,则默认方法将被忽略,这可能不是默认作者在声明它为 final方法时所期望的。 (这种继承行为反射(reflect)了默认方法的设计中心——接口(interface)进化。应该可以将默认方法(或现有接口(interface)方法的默认实现)添加到已经有实现的现有接口(interface),而无需更改实现接口(interface)的现有类的行为,保证在添加默认方法之前已经工作的类在默认方法存在的情况下以相同的方式工作。)

关于java - 为什么 Java 8 接口(interface)方法中不允许出现 "final"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23453287/

相关文章:

map 的 Java 方法引用 - IntelliJ 警告

java - Apache Spark : how to call UDF over dataset in Java?

c# - C# 中的 IArithmetic<T> 接口(interface)

Java泛型根据值返回枚举

java - 将 Arraylist 作为参数传递给函数

java - 我的程序出了什么问题?它没有显示任何时钟计时器?

css - 为什么CSS在规则中使用冒号 ":"?

python - 为什么在定义时评估默认参数?

java - 在Java8中使用过滤器和映射提取数据库列数据

Java 8 - 流、过滤器和可选