问题描述
默认方法是我们Java工具箱中的一个不错的新工具。但是,我尝试编写一个接口,用于定义 toString
方法的默认
版本。 Java告诉我这是禁止的,因为在 java.lang.Object
中声明的方法可能不是 default
ed。为什么会这样呢?
Default methods are a nice new tool in our Java toolbox. However, I tried to write an interface that defines a default
version of the toString
method. Java tells me that this is forbidden, since methods declared in java.lang.Object
may not be default
ed. Why is this the case?
我知道有基类永远胜利的规则,所以默认情况下(双关语),任何无论如何,
Object
中的方法将覆盖默认 Object
方法的实现。但是,我认为没有理由为规范中的 Object
中的方法提供异常。特别是对于 toString
,有一个默认实现可能非常有用。
I know that there is the "base class always wins" rule, so by default (pun ;), any default
implementation of an Object
method would be overwritten by the method from Object
anyway. However, I see no reason why there shouldn't be an exception for methods from Object
in the spec. Especially for toString
it might be very useful to have a default implementation.
那么,Java之所以有什么原因设计师决定不允许默认
方法覆盖 Object
中的方法?
So, what is the reason why Java designers decided to not allow default
methods overriding methods from Object
?
推荐答案
这是另一个语言设计问题似乎显然是一个好主意,直到你开始挖掘,你意识到它实际上是一个坏主意。
This is yet another of those language design issues that seems "obviously a good idea" until you start digging and you realize that its actually a bad idea.
在这个主题上有很多(以及其他主题。)有几种设计力量融合在一起,使我们达到目前的设计:
This mail has a lot on the subject (and on other subjects too.) There were several design forces that converged to bring us to the current design:
- 保持继承模型简单的愿望;
- 一旦你看过明显的例子(例如,转动
AbstractList)这一事实
进入接口),你意识到继承equals / hashCode / toString与单继承和状态紧密相关,接口是多重继承和无状态的; - 那它可能为一些令人惊讶的行为打开了大门。
- The desire to keep the inheritance model simple;
- The fact that once you look past the obvious examples (e.g., turning
AbstractList
into an interface), you realize that inheriting equals/hashCode/toString is strongly tied to single inheritance and state, and interfaces are multiply inherited and stateless; - That it potentially opened the door to some surprising behaviors.
你已经触及了保持简单的目标;继承和冲突解决规则设计得非常简单(类胜过接口,派生接口胜过超接口,任何其他冲突都由实现类解决。)当然这些规则可以调整为异常,但是我认为当你开始使用该字符串时,你会发现增量复杂性并不像你想象的那么小。
You've already touched on the "keep it simple" goal; the inheritance and conflict-resolution rules are designed to be very simple (classes win over interfaces, derived interfaces win over superinterfaces, and any other conflicts are resolved by the implementing class.) Of course these rules could be tweaked to make an exception, but I think you'll find when you start pulling on that string, that the incremental complexity is not as small as you might think.
当然,有一定程度的好处可以证明更复杂,但在这种情况下它并不存在。我们在这里讨论的方法是equals,hashCode和toString。这些方法本质上都是关于对象状态的,并且它是拥有状态而不是接口的类,它最有能力确定该等级对于该类的意义(特别是当相等的契约非常强时;请参阅有效Java有一些令人惊讶的后果);接口编写器太远了。
Of course, there's some degree of benefit that would justify more complexity, but in this case it's not there. The methods we're talking about here are equals, hashCode, and toString. These methods are all intrinsically about object state, and it is the class that owns the state, not the interface, who is in the best position to determine what equality means for that class (especially as the contract for equality is quite strong; see Effective Java for some surprising consequences); interface writers are just too far removed.
很容易拿出 AbstractList
示例;如果我们可以摆脱 AbstractList
并将行为放入 List
界面,那将是可爱的。但是,一旦你超越了这个明显的例子,就没有很多其他好的例子可以找到。在root中, AbstractList
是为单继承而设计的。但接口必须设计为多重继承。
It's easy to pull out the AbstractList
example; it would be lovely if we could get rid of AbstractList
and put the behavior into the List
interface. But once you move beyond this obvious example, there are not many other good examples to be found. At root, AbstractList
is designed for single inheritance. But interfaces must be designed for multiple inheritance.
此外,假设您正在撰写此课程:
Further, imagine you are writing this class:
class Foo implements com.libraryA.Bar, com.libraryB.Moo {
// Implementation of Foo, that does NOT override equals
}
Foo
编写器查看超类型,看不到equals的实现,并得出结论以获得引用相等性,他只需要do是从 Object
继承等于。然后,下周,Bar帮助的库维护者添加一个默认的等于
实现。哎呀!现在, Foo
的语义已被另一个维护域中的接口破坏,有用地为常用方法添加默认值。
The Foo
writer looks at the supertypes, sees no implementation of equals, and concludes that to get reference equality, all he need do is inherit equals from Object
. Then, next week, the library maintainer for Bar "helpfully" adds a default equals
implementation. Ooops! Now the semantics of Foo
have been broken by an interface in another maintenance domain "helpfully" adding a default for a common method.
默认值应该是默认值。将缺省值添加到没有的接口(层次结构中的任何位置)不应该影响具体实现类的语义。但是,如果默认值可以覆盖Object方法,那就不是真的。
Defaults are supposed to be defaults. Adding a default to an interface where there was none (anywhere in the hierarchy) should not affect the semantics of concrete implementing classes. But if defaults could "override" Object methods, that wouldn't be true.
所以,虽然它看起来像一个无害的功能,但它实际上是非常有害的:它增加了很多复杂性,几乎没有增量表现力,而且它太容易了对于单独编译的接口的良好,无害的更改,以破坏实现类的预期语义。
So, while it seems like a harmless feature, it is in fact quite harmful: it adds a lot of complexity for little incremental expressivity, and it makes it far too easy for well-intentioned, harmless-looking changes to separately compiled interfaces to undermine the intended semantics of implementing classes.
这篇关于Java8:为什么禁止从java.lang.Object为方法定义默认方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!