简体   繁体   English

Java8:为什么禁止为java.lang.Object中的方法定义默认方法

[英]Java8: Why is it forbidden to define a default method for a method from java.lang.Object

Default methods are a nice new tool in our Java toolbox. 默认方法是Java工具箱中一个不错的新工具。 However, I tried to write an interface that defines a default version of the toString method. 但是,我试图编写一个接口,该接口定义toString方法的default版本。 Java tells me that this is forbidden, since methods declared in java.lang.Object may not be default ed. Java告诉我,这是禁止的,因为java.lang.Object声明的方法可能不是default ed。 Why is this the case? 为什么会这样呢?

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. 我知道,有“基类总是胜”的规则,那么在默认情况下(双关语),任何default的实施Object方法将被重写从方法Object呢。 However, I see no reason why there shouldn't be an exception for methods from Object in the spec. 但是,我认为没有理由为什么规范中的Object方法不应该有例外。 Especially for toString it might be very useful to have a default implementation. 特别是对于toString ,使用默认实现可能非常有用。

So, what is the reason why Java designers decided to not allow default methods overriding methods from Object ? 那么,Java设计者决定不允许default方法覆盖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: 这封邮件涉及很多主题(以及其他主题。)有多种设计力量融合在一起,使我们进入了当前的设计:

  • The desire to keep the inheritance model simple; 保持继承模型简单的愿望;
  • The fact that once you look past the obvious examples (eg, 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; 一旦查看了显而易见的示例(例如,将AbstractList转换为接口),您就会意识到,继承equals / hashCode / toString与单继承和状态紧密相关,并且接口是多重继承且无状态的;
  • 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. 继承和冲突解决规则的设计非常简单(类胜过接口,派生接口胜过超接口,其他任何冲突都由实现类解决。)当然可以对这些规则进行调整以使其成为异常,但是我想您会发现,当您开始使用该字符串时,增量复杂性并没有您想象的那么小。

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. 我们在这里讨论的方法是equals,hashCode和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); 这些方法本质上都是关于对象状态的,并且拥有状态而不是接口的类是确定状态对该类意味着什么的最佳位置(尤其是当平等的契约非常牢固时;请参见有效)。 Java带来一些令人惊讶的后果); interface writers are just too far removed. 接口编写器距离太远了。

It's easy to pull out the AbstractList example; 提取AbstractList示例很容易; it would be lovely if we could get rid of AbstractList and put the behavior into the List interface. 如果我们可以摆脱AbstractList并将行为放入List接口,那将是很可爱的。 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. 从根本上说, AbstractList是为单继承设计的。 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
}

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 . Foo作者研究了超类型,没有看到equals的实现,并得出结论,要获得引用相等,他要做的就是从Object继承equals。 Then, next week, the library maintainer for Bar "helpfully" adds a default equals implementation. 然后,下周,Bar的库维护者“有帮助地”添加了默认的equals实现。 Ooops! 哎呀! Now the semantics of Foo have been broken by an interface in another maintenance domain "helpfully" adding a default for a common method. 现在, Foo的语义已被另一个“维护”域中的接口“有用地”破坏了,它为通用方法添加了默认值。

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. 但是如果默认值可以“覆盖” Object方法,那将不是真的。

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. 因此,尽管它看起来像是无害的功能,但实际上却是非常有害的:它增加了很多复杂性,几乎没有增量表达能力,而且对于原本意图良好,无害的更改进行单独编译的接口,破坏太容易了。实现类的预期语义。

It is forbidden to define default methods in interfaces for methods in java.lang.Object , since the default methods would never be "reachable". 禁止在接口中为java.lang.Object方法定义默认方法,因为默认方法永远不会“可访问”。

Default interface methods can be overwritten in classes implementing the interface and the class implementation of the method has a higher precedence than the interface implementation, even if the method is implemented in a superclass. 可以在实现该接口的类中覆盖默认接口方法,并且即使该方法是在超类中实现的,该方法的类实现也比接口实现的优先级更高。 Since all classes inherit from java.lang.Object , the methods in java.lang.Object would have precedence over the default method in the interface and be invoked instead. 由于所有的类继承java.lang.Object ,在方法java.lang.Object必须优先于接口的默认方法来代替调用。

Brian Goetz from Oracle provides a few more details on the design decision in this mailing list post . Oracle的Brian Goetz在此邮件列表中提供了有关设计决策的更多详细信息。

I do not see into the head of Java language authors, so we may only guess. 我看不到Java语言作者的头,所以我们只能猜测。 But I see many reasons and agree with them absolutely in this issue. 但是我看到许多原因,并且在这个问题上完全同意它们。

The main reason for introducing default methods is to be able to add new methods to interfaces without breaking the backward compatibility of older implementations. 引入默认方法的主要原因是能够在不破坏旧版实现的向后兼容性的情况下向接口添加新方法。 The default methods may also be used to provide "convenience" methods without the necessity to define them in each of the implementing classes. 默认方法也可以用于提供“便利”方法,而不必在每个实现类中都定义它们。

None of these applies to toString and other methods of Object. 这些都不适用于toString和其他Object方法。 Simply put, default methods were designed to provide the default behavior where there is no other definition. 简而言之,默认方法旨在提供没有其他定义的默认行为。 Not to provide implementations that will "compete" with other existing implementations. 不提供将与其他现有实现“竞争”的实现。

The "base class always wins" rule has its solid reasons, too. “基层总是赢”的规则也有其坚实的理由。 It is supposed that classes define real implementations, while interfaces define default implementations, which are somewhat weaker. 假设类定义了实际的实现,而接口定义了默认的实现,而后者则较弱。

Also, introducing ANY exceptions to general rules cause unnecessary complexity and raise other questions. 另外,将ANY例外引入通用规则会导致不必要的复杂性并引发其他问题。 Object is (more or less) a class as any other, so why should it have different behaviour? 对象(或多或少)是一个类,为什么它应该具有不同的行为?

All and all, the solution you propose would probably bring more cons than pros. 总而言之,您提出的解决方案可能会带来比专家更多的弊端。

The reasoning is very simple, it is because Object is the base class for all the Java classes. 推理非常简单,这是因为Object是所有Java类的基类。 So even if we have Object's method defined as default method in some interface, it will be useless because Object's method will always be used. 因此,即使我们在某些接口中将Object的方法定义为默认方法,也将是无用的,因为将始终使用Object的方法。 That is why to avoid confusion, we cannot have default methods that are overriding Object class methods. 这就是为什么要避免混淆的原因,我们不能拥有覆盖对象类方法的默认方法。

To give a very pedantic answer, it is only forbidden to define a default method for a public method from java.lang.Object . 为了给出一个非常古怪的答案,仅禁止从java.lang.Object公共方法定义default方法。 There are 11 methods to consider, which can be categorized in three ways to answer this question. 有11种方法可供考虑,可以通过三种方式进行分类来回答此问题。

  1. Six of the Object methods cannot have default methods because they are final and cannot be overridden at all: getClass() , notify() , notifyAll() , wait() , wait(long) , and wait(long, int) . 其中六个Object方法不能具有default方法,因为它们是final方法,并且根本不能被覆盖: getClass()notify()notifyAll()wait()wait(long)wait(long, int)
  2. Three of the Object methods cannot have default methods for the reasons given above by Brian Goetz: equals(Object) , hashCode() , and toString() . 由于上面的Brian Goetz给出的原因,三个Object方法不能具有default方法: equals(Object)hashCode()toString()
  3. Two of the Object methods can have default methods, though the value of such defaults is questionable at best: clone() and finalize() . Object方法中的两个可以具有default方法,尽管这样的默认值充其量是令人怀疑的: clone()finalize()

     public class Main { public static void main(String... args) { new FOO().clone(); new FOO().finalize(); } interface ClonerFinalizer { default Object clone() {System.out.println("default clone"); return this;} default void finalize() {System.out.println("default finalize");} } static class FOO implements ClonerFinalizer { @Override public Object clone() { return ClonerFinalizer.super.clone(); } @Override public void finalize() { ClonerFinalizer.super.finalize(); } } } 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么java.lang.Object中的finalize()方法受到“保护”? - Why is the finalize() method in java.lang.Object “protected”? 为什么在 java.lang.Object 中保护了 clone() 方法? - Why is the clone() method protected in java.lang.Object? 我们需要java.lang.Object中的方法getClass()吗? - Do we need the method getClass() from java.lang.Object? 如何从MonoDroid中的重写方法返回Java.Lang.Object - How to return Java.Lang.Object from a overridden method in MonoDroid 为什么java.lang.Cloneable不会覆盖java.lang.Object中的clone()方法? - Why does java.lang.Cloneable not override the clone() method in java.lang.Object? Android Studio - 无法解析方法“setText(java.lang.Object)” - Android Studio - Cannot resolve method 'setText(java.lang.Object)' 我们可以在java.lang.Object中添加额外的方法吗? - Can we add extra method in java.lang.Object? 为什么编译器在泛型返回类型的方法上抱怨java.lang.Object - Why compiler complains about java.lang.Object on generic return type of method 为什么 java.lang.Object class clone() 方法没有主体? - Why doesn't the java.lang.Object class clone() method have a body? 找不到引用的方法'com.google.firebase.tasks.Task setValue(java.lang.Object,java.lang.Object)' - can't find referenced method 'com.google.firebase.tasks.Task setValue(java.lang.Object,java.lang.Object)'
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM