本文介绍了Java8:为什么禁止为来自 java.lang.Object 的方法定义默认方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

默认方法是我们 Java 工具箱中的一个不错的新工具.但是,我尝试编写一个接口来定义 toString 方法的 default 版本.Java 告诉我这是禁止的,因为在 java.lang.Object 中声明的方法可能不是 defaulted.为什么会这样?

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 defaulted. Why is this the case?

我知道有基类总是获胜"的规则,所以默认情况下(双关语;),Object 方法的任何 default 实现都将被覆盖无论如何,来自 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 设计者决定不允许 default 方法覆盖来自 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.这些方法本质上都是关于对象状态的,拥有状态的类,而不是接口,谁最有资格确定该类的相等性意味着什么(特别是因为相等性的契约非常强;参见 EffectiveJava 的一些令人惊讶的后果);界面编写者太远了.

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 接口中,那就太好了.但是一旦你超越了这个明显的例子,就找不到很多其他好的例子了.从根本上说,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 的实现,并得出结论,要获得引用相等性,他需要做的就是从 Object 继承 equals.然后,下周,Bar 的库维护者有帮助地"添加了一个默认的 equals 实现.哎呀!现在 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 的方法定义默认方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-01 18:27