为什么 Java 8 接口方法中不允许使用“final”?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23453287/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why is "final" not allowed in Java 8 interface methods?
提问by Lukas Eder
One of the most useful features of Java 8 are the new default
methods on interfaces. There are essentially two reasons (there may be others) why they have been introduced:
Java 8 最有用的特性之一是default
接口上的新方法。引入它们基本上有两个原因(可能还有其他原因):
- Providing actual default implementations. Example:
Iterator.remove()
- Allowing for JDK API evolution. Example:
Iterable.forEach()
- 提供实际的默认实现。例子:
Iterator.remove()
- 允许 JDK API 演进。例子:
Iterable.forEach()
From an API designer's perspective, I would have liked to be able to use other modifiers on interface methods, e.g. final
. This would be useful when adding convenience methods, preventing "accidental" overrides in implementing classes:
从 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);
}
The above is already common practice if Sender
were a class:
如果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);
}
Now, default
and final
are obviously contradicting keywords, but the default keyword itself would not have been strictly required, so I'm assuming that this contradiction is deliberate, to reflect the subtle differences between "class methods with body"(just methods) and "interface methods with body"(default methods), i.e. differences which I have not yet understood.
现在,default
和final
显然是自相矛盾的关键字,但默认关键字本身不会被严格要求,所以我假设这个矛盾是故意的,以反映“带主体的类方法”(只是方法)和“接口”之间的细微差别方法体”(默认方法),即我尚未理解的差异。
At some point of time, support for modifiers like static
and final
on interface methods was not yet fully explored, citing Brian Goetz:
在一定的时间点,对于像修饰符的支持static
和final
接口方法上还没有充分探讨,援引布赖恩戈茨:
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
另一部分是我们将在多大程度上支持接口中的类构建工具,例如 final 方法、私有方法、受保护方法、静态方法等。 答案是:我们还不知道
Since that time in late 2011, obviously, support for static
methods in interfaces was added. Clearly, this added a lot of value to the JDK libraries themselves, such as with Comparator.comparing()
.
从 2011 年末的那个时候开始,很明显,static
增加了对接口中方法的支持。显然,这为 JDK 库本身增加了很多价值,例如使用Comparator.comparing()
.
Question:
题:
What is the reason final
(and also static final
) never made it to Java 8 interfaces?
从未进入 Java 8 接口的原因final
(以及static final
)是什么?
采纳答案by Brian Goetz
This question is, to some degree, related to What is the reason why “synchronized” is not allowed in Java 8 interface methods?
这个问题在某种程度上与Java 8 接口方法中不允许“同步”的原因是什么有关?
The key thing to understand about default methods is that the primary design goal is interface evolution, not "turn interfaces into (mediocre) traits". While there's some overlap between the two, and we tried to be accommodating to the latter where it didn't get in the way of the former, these questions are best understood when viewed in this light. (Note too that class methods aregoing to be different from interface methods, no matter what the intent, by virtue of the fact that interface methods can be multiply inherited.)
理解默认方法的关键是主要设计目标是接口进化,而不是“将接口变成(平庸的)特征”。虽然两者之间存在一些重叠,并且我们试图在不妨碍前者的情况下适应后者,但从这个角度来看,最好理解这些问题。(还要注意,类方法都将是从接口的方法不同,不管是什么目的,凭借该接口方法可以多重继承的事实。)
The basic idea of a default method is: it is an interface method with a default implementation, and a derived class can provide a more specific implementation. And because the design center was interface evolution, it was a critical design goal that default methods be able to be added to interfaces after the factin a source-compatible and binary-compatible manner.
默认方法的基本思想是:它是具有默认实现的接口方法,派生类可以提供更具体的实现。并且由于设计中心是接口进化,因此能够以源代码兼容和二进制兼容的方式事后将默认方法添加到接口是一个关键的设计目标。
The too-simple answer to "why not final default methods" is that then the body would then not simply be the default implementation, it would be the only implementation. While that's a little too simple an answer, it gives us a clue that the question is already heading in a questionable direction.
对“为什么不是最终默认方法”的过于简单的回答是,那么主体将不仅仅是默认实现,而是唯一的实现。虽然这个答案有点过于简单,但它为我们提供了一个线索,即问题已经朝着有问题的方向发展。
Another reason why final interface methods are questionable is that they create impossible problems for implementors. For example, suppose you have:
最终接口方法有问题的另一个原因是它们给实现者带来了不可能的问题。例如,假设您有:
interface A {
default void foo() { ... }
}
interface B {
}
class C implements A, B {
}
Here, everything is good; C
inherits foo()
from A
. Now supposing B
is changed to have a foo
method, with a default:
在这里,一切都很好;C
继承foo()
自A
. 现在假设B
更改为具有foo
默认值的方法:
interface B {
default void foo() { ... }
}
Now, when we go to recompile C
, the compiler will tell us that it doesn't know what behavior to inherit for foo()
, so C
has to override it (and could choose to delegate to A.super.foo()
if it wanted to retain the same behavior.) But what if B
had made its default final
, and A
is not under the control of the author of C
? Now C
is irretrievably broken; it can't compile without overriding foo()
, but it can't override foo()
if it was final in B
.
现在,当我们去 recompile 时C
,编译器会告诉我们它不知道要继承什么行为foo()
,所以C
必须覆盖它(A.super.foo()
如果它想保留相同的行为,可以选择委托。)但是如果B
已经默认了final
,并且A
不在作者的控制之下C
?现在C
已经无可挽回地破碎了;它不能在没有覆盖的情况下编译foo()
,但foo()
如果它在B
.
This is just one example, but the point is that finality for methods is really a tool that makes more sense in the world of single-inheritance classes (generally which couple state to behavior), than to interfaces which merely contribute behavior and can be multiply inherited. It's too hard to reason about "what other interfaces might be mixed into the eventual implementor", and allowing an interface method to be final would likely cause these problems (and they would blow up not on the person who wrote the interface, but on the poor user who tries to implement it.)
这只是一个例子,但关键是方法的最终性确实是一种工具,在单继承类(通常将状态与行为耦合)的世界中比在仅贡献行为并且可以乘法的接口中更有意义遗传。很难推理“哪些其他接口可能会混入最终的实现者中”,并且允许接口方法为 final 可能会导致这些问题(并且它们不会对编写接口的人造成影响,而是对编写接口的人造成影响)试图实施它的可怜用户。)
Another reason to disallow them is that they wouldn't mean what you think they mean. A default implementation is only considered if the class (or its superclasses) don't provide a declaration (concrete or abstract) of the method. If a default method were final, but a superclass already implemented the method, the default would be ignored, which is probably not what the default author was expecting when declaring it final. (This inheritance behavior is a reflection of the design center for default methods -- interface evolution. It should be possible to add a default method (or a default implementation to an existing interface method) to existing interfaces that already have implementations, without changing the behavior of existing classes that implement the interface, guaranteeing that classes that already worked before default methods were added will work the same way in the presence of default methods.)
禁止它们的另一个原因是它们的意思与您认为的意思不同。仅当类(或其超类)不提供方法的声明(具体或抽象)时才考虑默认实现。如果默认方法是 final 的,但超类已经实现了该方法,则默认方法将被忽略,这可能不是默认作者在将其声明为 final 时所期望的。(这种继承行为反映了默认方法的设计中心——接口演化。应该可以在已有实现的现有接口中添加默认方法(或对现有接口方法的默认实现),而无需更改实现接口的现有类的行为,
回答by Marco13
It will be hard to find and identify "THE" answer, for the resons mentioned in the comments from @EJP : There are roughly 2 (+/- 2) people in the world who can give the definite answer at all. And in doubt, the answer might just be something like "Supporting final default methods did not seem to be worth the effort of restructuring the internal call resolution mechanisms". This is speculation, of course, but it is at least backed by subtle evidences, like this Statement (by one of the two persons) in the OpenJDK mailing list:
这将是很难发现和识别“THE”的答案,在从@EJP的评论中提到的resons:有大约2(+/- 2)人在世界上,谁可以给定答案可言。并且有疑问,答案可能只是类似于“支持最终默认方法似乎不值得重新构造内部调用解析机制的努力”。当然,这是推测,但它至少有微妙的证据支持,比如OpenJDK 邮件列表中的这个声明(由两个人之一):
"I suppose if "final default" methods were allowed, they might need rewriting from internal invokespecial to user-visible invokeinterface."
“我想如果允许使用“最终默认”方法,它们可能需要从内部 invokespecial 重写为用户可见的 invokeinterface。”
and trivial facts like that a method is simply not consideredto be a (really) final method when it is a default
method, as currently implemented in the Method::is_final_methodmethod in the OpenJDK.
和诸如方法之类的琐碎事实,当它是一个方法时,它根本不被认为是(真正的)最终default
方法,正如目前在 OpenJDK的Method::is_final_method方法中实现的那样。
Further really "authorative" information is indeed hard to find, even with excessive websearches and by reading commit logs. I thought that it might be related to potential ambiguities during the resolution of interface method calls with the invokeinterface
instruction and and class method calls, corresponding to the invokevirtual
instruction: For the invokevirtual
instruction, there may be a simple vtablelookup, because the method must either be inherited from a superclass, or implemented by the class directly. In contrast to that, an invokeinterface
call must examine the respective call site to find out whichinterface this call actually refers to (this is explained in more detail in the InterfaceCallspage of the HotSpot Wiki). However, final
methods do either not get inserted into the vtableat all, or replace existing entries in the vtable(see klassVtable.cpp. Line 333), and similarly, default methods are replacing existing entries in the vtable(see klassVtable.cpp, Line 202). So the actualreason (and thus, the answer) must be hidden deeper inside the (rather complex) method call resolution mechanisms, but maybe these references will nevertheless be considered as being helpful, be it only for others that manage to derive the actual answer from that.
进一步真正“权威”的信息确实很难找到,即使是过度的网络搜索和阅读提交日志。我想可能是在解析接口方法调用与invokeinterface
指令和类方法调用过程中潜在的歧义有关,对应invokevirtual
指令:对于invokevirtual
指令,可能有一个简单的vtable查找,因为方法要么必须继承来自超类,或由类直接实现。与此相反,invokeinterface
调用必须检查相应的调用站点以找出该调用实际指的是哪个接口(在 HotSpot Wiki的InterfaceCalls页面中有更详细的解释)。然而,final
方法都既不会被插入到虚函数表的全部,或替换现有的条目虚函数表(见klassVtable.cpp 333线),同样,默认的方法是在替换现有的条目虚函数表(见klassVtable.cpp,202线) . 因此,实际原因(以及答案)必须更深地隐藏在(相当复杂的)方法调用解析机制中,但也许这些参考文献仍然会被认为是有帮助的,是否仅适用于设法得出实际答案的其他人从那。
回答by skiwi
I wouldn't think it is neccessary to specify final
on a convienience interface method, I can agree though that it maybe helpful, but seemingly the costs have outweight the benefits.
我认为没有必要指定final
一个方便的接口方法,我同意虽然它可能有帮助,但似乎成本超过了收益。
What you are supposed to do, either way, is to write proper javadoc for the default method, showing exactly what the method is and is not allowed to do. In that way the classes implementing the interface "are not allowed" to change the implementation, though there are no guarantees.
无论哪种方式,您应该做的是为默认方法编写适当的 javadoc,准确显示该方法是什么以及不允许做什么。通过这种方式,实现接口的类“不允许”更改实现,尽管没有保证。
Anyone could write a Collection
that adheres to the interface and then does things in the methods that are absolutely counter intuitive, there is no way to shield yourself from that, other than writing extensive unit tests.
任何人都可以编写一个Collection
遵循界面的方法,然后以绝对违反直觉的方法进行操作,除了编写大量的单元测试之外,没有办法避免这样做。
回答by Edwin Dalorzo
In the lambda mailing list there are plenty of discussions about it. One of those that seems to contain a lot of discussion about all that stuff is the following: On Varied interface method visibility (was Final defenders).
在 lambda 邮件列表中有很多关于它的讨论。其中一个似乎包含很多关于所有这些东西的讨论如下:关于各种接口方法可见性(是最终捍卫者)。
In this discussion, Talden, the author of the original questionasks something very similar to your question:
在本次讨论中,原始问题的作者 Talden提出了与您的问题非常相似的问题:
The decision to make all interface members public was indeed an unfortunate decision. That any use of interface in internal design exposes implementation private details is a big one.
It's a tough one to fix without adding some obscure or compatibility breaking nuances to the language. A compatibility break of that magnitude and potential subtlety would seen unconscionable so a solution has to exist that doesn't break existing code.
Could reintroducing the 'package' keyword as an access-specifier be viable. It's absence of a specifier in an interface would imply public-access and the absence of a specifier in a class implies package-access. Which specifiers make sense in an interface is unclear - especially if, to minimise the knowledge burden on developers, we have to ensure that access-specifiers mean the same thing in both class and interface if they're present.
In the absence of default methods I'd have speculated that the specifier of a member in an interface has to be at least as visible as the interface itself (so the interface can actually be implemented in all visible contexts) - with default methods that's not so certain.
Has there been any clear communication as to whether this is even a possible in-scope discussion? If not, should it be held elsewhere.
将所有接口成员公开的决定确实是一个不幸的决定。在内部设计中使用任何接口都会暴露实现的私有细节,这是一个很大的问题。
如果不给语言添加一些晦涩的或破坏兼容性的细微差别,就很难解决这个问题。如此大规模和潜在的微妙性的兼容性中断将被视为不合情理,因此必须存在不破坏现有代码的解决方案。
重新引入 'package' 关键字作为访问说明符是否可行。接口中没有说明符意味着公共访问,而类中没有说明符意味着包访问。接口中哪些说明符有意义尚不清楚 - 特别是如果为了最大限度地减少开发人员的知识负担,我们必须确保访问说明符在类和接口中的含义相同(如果它们存在)。
在没有默认方法的情况下,我推测接口中成员的说明符必须至少与接口本身一样可见(因此接口实际上可以在所有可见上下文中实现)-默认方法不是那么确定。
关于这是否是可能的范围内讨论,是否有任何明确的沟通?如果没有,是否应该在其他地方举行。
Eventually Brian Goetz's answerwas:
最终Brian Goetz 的回答是:
Yes, this is already being explored.
However, let me set some realistic expectations -- language / VM features have a long lead time, even trivial-seeming ones like this. The time for proposing new language feature ideas for Java SE 8 has pretty much passed.
是的,这已经在探索中。
然而,让我设定一些现实的期望——语言/VM 特性有很长的交付周期,即使是像这样看似微不足道的特性。为 Java SE 8 提出新的语言特性想法的时代已经过去了。
So, most likely it was never implemented because it was never part of the scope. It was never proposed in time to be considered.
因此,很可能它从未实施过,因为它从未属于范围的一部分。它从未被及时提出以供考虑。
In another heated discussion about final defender methodson the subject, Brian said again:
在另一个关于这个主题的最终防御方法的激烈讨论中,布赖恩再次说道:
And you have gotten exactly what you wished for. That's exactly what this feature adds -- multiple inheritance of behavior. Of course we understand that people will use them as traits. And we've worked hard to ensure that the the model of inheritance they offer is simple and clean enough that people can get good results doing so in a broad variety of situations. We have, at the same time, chosen not to push them beyond the boundary of what works simply and cleanly, and that leads to "aw, you didn't go far enough" reactions in some case. But really, most of this thread seems to be grumbling that the glass is merely 98% full. I'll take that 98% and get on with it!
你已经得到了你想要的。这正是此功能添加的内容——行为的多重继承。当然,我们理解人们会将它们用作特征。我们努力确保他们提供的继承模型足够简单和干净,以便人们在各种情况下都能获得良好的结果。同时,我们选择不将它们推到简单而干净的工作范围之外,这在某些情况下会导致“哦,你做得还不够”反应。但实际上,这个帖子的大部分内容似乎都在抱怨玻璃杯只有 98% 是满的。我会拿走那 98% 并继续下去!
So this reinforces my theory that it simply was not part of the scope or part of their design. What they did was to provide enough functionality to deal with the issues of API evolution.
所以这加强了我的理论,即它根本不是他们设计的范围或一部分。他们所做的是提供足够的功能来处理 API 演进的问题。