为什么Java 8接口方法中不允许使用“最终”?

本文翻译自:Why is “final” not allowed in Java 8 interface methods?

One of the most useful features of Java 8 are the new default methods on interfaces. Java 8最有用的功能之一是接口上的新default方法。 There are essentially two reasons (there may be others) why they have been introduced: 引入它们的原因基本上有两个(可能还有其他原因):

From an API designer's perspective, I would have liked to be able to use other modifiers on interface methods, eg final . 从API设计人员的角度来看,我希望能够在接口方法上使用其他修饰符,例如final This would be useful when adding convenience methods, preventing "accidental" overrides in implementing classes: 这在添加便捷方法时非常有用,可以防止在实现类时“意外”覆盖:

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), ie differences which I have not yet understood. 现在, defaultfinal显然是矛盾的关键字,但是default关键字本身并不是严格要求的 ,因此我假设这种矛盾是故意的,以反映“类方法与主体” (正义方法)和“带有主体的接口方法” (默认方法),即我尚未理解的差异。

At some point of time, support for modifiers like static and final on interface methods was not yet fully explored, citing Brian Goetz : 在某些时候,还引用了Brian Goetz的话 ,尚未完全探索对修饰符(如staticfinal的接口方法)的支持:

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 另一部分是我们要在接口中支持类构建工具的程度,例如最终方法,私有方法,受保护的方法,静态方法等。答案是:我们尚不知道

Since that time in late 2011, obviously, support for static methods in interfaces was added. 从2011年末开始,很明显,增加了对接口中static方法的支持。 Clearly, this added a lot of value to the JDK libraries themselves, such as with Comparator.comparing() . 显然,这为JDK库本身增加了很多价值,例如Comparator.comparing()

Question: 题:

What is the reason final (and also static final ) never made it to Java 8 interfaces? final (以及static final )从未进入Java 8接口的原因是什么?


#1楼

参考:https://stackoom.com/question/1aPGp/为什么Java-接口方法中不允许使用-最终


#2楼

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 . 这将是很难发现和识别“THE”的答案,在从@EJP的评论中提到的resons:有大约2(+/- 2)人在世界上谁可以给明确的答复都没有 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 : 当然,这是推测,但是至少有一些微妙的证据支持,例如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 considered to be a (really) final method when it is a default method, as currently implemented in the Method::is_final_method method 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 vtable lookup, because the method must either be inherited from a superclass, or implemented by the class directly. 我认为这可能接口方法的解析过程中与调用相关的潜在歧义invokeinterface指令和类方法调用,对应invokevirtual指令:对于invokevirtual指令,有可能是一个简单的虚函数表的查找,因为该方法必须可以从超类继承,也可以直接由该类实现。 In contrast to that, an invokeinterface call must examine the respective call site to find out which interface this call actually refers to (this is explained in more detail in the InterfaceCalls page of the HotSpot Wiki). 与此相反,一个invokeinterface调用必须检查相应的调用点,找出哪个接口这个调用实际上指的是(这在更详细的解释InterfaceCalls的热点维基页面)。 However, final methods do either not get inserted into the vtable at 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 ). 然而, final方法都既不会被插入到虚函数表的全部或替换虚函数表的现有条目(见klassVtable.cpp。线333 ),同样,默认的方法是在虚函数表替换现有的条目(见klassVtable.cpp,第202行 )。 So the actual reason (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. 因此, 实际原因(以及答案)必须更深地隐藏在(相当复杂的)方法调用解析机制中,但是也许这些引用仍然被认为是有帮助的,因为它仅对那些设法得出实际答案的人有用。从那开始。


#3楼

I wouldn't think it is neccessary to specify final on a convienience interface method, I can agree though that it may be helpful, but seemingly the costs have outweight the benefits. 我不认为这里有必要指定final一个convienience接口的方法,我可以同意,虽然它可能是有用的,但看似成本outweight的好处。

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. 无论哪种方式,您都应该为默认方法编写适当的javadoc,以准确显示该方法允许和不允许执行的操作。 In that way the classes implementing the interface "are not allowed" to change the implementation, though there are no guarantees. 这样,尽管没有保证,但实现接口的类“不允许”更改实现。

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 ,然后以绝对违反直觉的方法进行操作,除了编写大量的单元测试之外,没有其他方法可以使自己免受其害。


#4楼

In the lambda mailing list there are plenty of discussions about it . 在lambda邮件列表中,对此进行了大量讨论 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) . 关于所有这些东西的讨论似乎很多,其中之一是:在各种接口方法上的可见性(是Final Defender)

In this discussion, Talden, the author of the original question asks something very similar to your question: 在此讨论中, 原始问题的作者塔尔登提出的问题与您的问题非常相似:

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. 可以重新引入'package'关键字作为访问说明符是可行的。 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. 如果没有,应该在其他地方举行。

Eventually Brian Goetz's answer was: 最终, 布莱恩·格茨(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. 但是,让我设定一些现实的期望-语言/ VM功能的交付时间很长,甚至像这样的琐碎事物也是如此。 The time for proposing new language feature ideas for Java SE 8 has pretty much passed. 为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 methods on the subject, Brian said again : 在关于此主题的最终防御者方法的另一激烈讨论中, Brian再次说

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. 但是,实际上,大部分线程似乎都在抱怨玻璃杯只有98%充满。 I'll take that 98% and get on with it! 我会拿那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演变问题。


#5楼

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 are going 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 fact in 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 . CA继承foo() Now supposing B is changed to have a foo method, with a default: 现在假设将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 ? 现在,当我们重新编译C ,编译器将告诉我们它不知道foo()继承什么行为,因此C必须重写它(并且可以选择将其委派给A.super.foo()但是如果B将其默认默认值定为final并且A不受C的作者控制,该怎么办? Now C is irretrievably broken; 现在, C不可恢复地被破坏了。 it can't compile without overriding foo() , but it can't override foo() if it was final in B . 它不能不覆盖foo()而进行编译,但是如果它在B是final,则不能覆盖foo()

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.) 很难说出“最终实现器中可能还混入了其他什么接口”,而允许接口方法成为最终方法可能会导致这些问题(它们不会使编写接口的人大发雷霆,而会炸毁该接口的人)。尝试实现该目标的可怜用户。)

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. 如果默认方法是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.) (此继承行为反映了设计中心对默认方法(接口演化的反映)。应该可以将默认方法(或对现有接口方法的默认实现)添加到已经具有实现的现有接口中,而无需更改实现该接口的现有类的行为,确保在添加默认方法之前已经工作的类在存在默认方法的情况下将以相同的方式工作。)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值