为什么Java8接口方法中不允许使用“final”?

Java8最有用的特性之一是接口上新的default方法。引入它们的主要原因有两个(可能还有其他原因):

从API设计师的角度来看,我希望能够在接口方法上使用其他修饰符,例如final。这在添加便利方法时非常有用,可以防止实现类中的“意外”重写:

接口发送器{
//发送空消息的便捷方法
默认的最终作废发送(){
发送(空);
}
//实现应该只实现这个方法
无效发送(字符串消息);
}

如果Sender是一个类,则上述做法已经很常见:

抽象类发送器{
//发送空消息的便捷方法
最终作废发送(){
发送(空);
}
//实现应该只实现这个方法
摘要无效发送(字符串消息);
}

现在,defaultfinal显然是相互矛盾的关键字,但是默认关键字本身不是严格要求的,因此我假设这一矛盾是故意的,以反映“带主体的类方法”(仅方法)和“带主体的接口方法”(默认方法),即我尚未理解的差异

在某些时候,对接口方法上的修改器(如staticfinal)的支持还没有得到充分的探索,引用布莱恩·戈茨的话

另一部分是我们将在多大程度上支持班级建设
接口中的工具,如final方法、private方法、protected方法
方法、静态方法等等。答案是:我们还不知道

显然,自2011年末以来,接口中增加了对静态方法的支持。显然,这为JDK库本身增加了很多价值,比如Comparator.comparating()

问题:

为什么final(以及static final)从未进入Java8接口

这个问题在某种程度上与Java 8接口方法中不允许“同步”的原因是什么

了解默认方法的关键是,主要的设计目标是界面进化,而不是“将界面变成(平庸的)特征”“虽然两者之间有一些重叠,我们试图适应后者,而后者并没有妨碍前者,但从这个角度来看,这些问题是最容易理解的。(还要注意,类方法将与接口方法不同,无论其意图如何,因为接口方法可以被多重继承。)

默认方法的基本思想是:它是具有默认实现的接口方法,派生类可以提供更具体的实现。因为设计中心是接口进化,所以一个关键的设计目标是,默认方法能够以源代码兼容和二进制兼容的方式添加到接口

对于“为什么不使用最终默认方法”的回答过于简单,因为这样一来,主体就不仅仅是默认实现,而是唯一的实现。虽然这个答案有点过于简单,但它给了我们一个线索,即这个问题已经朝着一个值得怀疑的方向发展

最终接口方法有问题的另一个原因是它们给实现者带来了不可能的问题。例如,假设您有:

接口A{
默认的void foo(){…}
}
接口B{
}
类C实现了A,B{
}

在这里,一切都很好CA继承foo()。现在假设B更改为具有foo方法,默认值为:

接口B{
默认的void foo(){…}
}

现在,当我们重新编译C时,编译器会告诉我们,它不知道继承foo()的行为,因此C必须重写它(如果它想保留相同的行为,可以选择委托给A.super.foo())但是如果B已经设置了默认的final,并且A不受C作者的控制,该怎么办?现在,C已经无法挽回地崩溃了;如果不重写foo(),它就无法编译,但是如果它是B中的final,它就不能重写foo()

这只是一个例子,但关键是方法的终结性在单继承类(通常将状态耦合到行为)的世界中比在接口中更有意义,因为接口只贡献行为,并且可以多重继承。很难推理“最终实现者可能会混合哪些其他接口”,而允许接口方法成为最终方法可能会导致这些问题(它们不会在编写接口的人身上爆发,而是在试图实现接口的可怜用户身上爆发)

不允许他们的另一个原因是他们的意思和你认为的不同。只有当类(或其超类)不提供方法的声明(具体或抽象)时,才会考虑使用默认实现。如果一个默认方法是final,但是一个超类已经实现了该方法,那么默认方法将被忽略,这可能不是默认作者在声明final时所期望的。(此继承行为反映了默认方法的设计中心——接口演化。应该可以添加默认方法(或现有接口方法的默认实现)对于已经有实现的现有接口,在不更改实现该接口的现有类的行为的情况下,保证在添加默认方法之前已经工作的类在存在默认方法的情况下将以相同的方式工作。)

发表评论