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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24016962/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 09:45:33  来源:igfitidea点击:

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

javainterfacejava-8default-method

提问by gexicide

Default methods are a nice new tool in our Java toolbox. However, I tried to write an interface that defines a defaultversion of the toStringmethod. Java tells me that this is forbidden, since methods declared in java.lang.Objectmay not be defaulted. Why is this the case?

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

I know that there is the "base class always wins" rule, so by default (pun ;), any defaultimplementation of an Objectmethod would be overwritten by the method from Objectanyway. However, I see no reason why there shouldn't be an exception for methods from Objectin the spec. Especially for toStringit might be very useful to have a default implementation.

我知道有“基类总是获胜”的规则,所以默认情况下(双关语;),任何方法的default实现Object都会被方法从Object任何地方覆盖。但是,我看不出为什么Object规范中的方法不应该有例外。特别是因为toString拥有默认实现可能非常有用。

So, what is the reason why Java designers decided to not allow defaultmethods overriding methods from Object?

那么,Java 设计者决定不允许default方法覆盖方法的原因是什么Object

采纳答案by Brian Goetz

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 mailhas 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 (e.g., turning AbstractListinto 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.
  • 保持继承模型简单的愿望;
  • 事实上,一旦你看过明显的例子(例如,AbstractList变成一个接口),你就会意识到继承 equals/hashCode/toString 与单继承和状态密切相关,而接口是多重继承和无状态的;
  • 它可能为一些令人惊讶的行为打开了大门。

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. 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.

当然,有一定程度的好处可以证明更多的复杂性是合理的,但在这种情况下它不存在。我们在这里讨论的方法是 equals、hashCode 和 toString。这些方法本质上都是关于对象状态的,拥有状态的类,而不是接口,谁最有资格确定该类的相等性意味着什么(特别是因为相等性的契约非常强;参见 Effective Java 的一些令人惊讶的后果);界面编写者太远了。

It's easy to pull out the AbstractListexample; it would be lovely if we could get rid of AbstractListand put the behavior into the Listinterface. But once you move beyond this obvious example, there are not many other good examples to be found. At root, AbstractListis designed for single inheritance. But interfaces must be designed for multiple inheritance.

AbstractList举出例子很容易;如果我们可以摆脱AbstractList并将行为放入List界面中,那就太好了。但是一旦你超越了这个明显的例子,就找不到很多其他好的例子了。从根本上说,AbstractList是为单继承而设计的。但是接口必须是为多重继承而设计的。

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 Foowriter 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 equalsimplementation. Ooops! Now the semantics of Foohave been broken by an interface in another maintenance domain "helpfully" adding a default for a common method.

Foo作家着眼于超类型,认为没有实现平等的,并得出结论,得到参考平等,所有他所要做的就是继承平等Object。然后,下周,Bar 的库维护者“有帮助地”添加了一个默认equals实现。哎呀!现在, 的语义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.

因此,虽然它看起来是一个无害的特性,但它实际上是非常有害的:它为很少的增量表达增加了很多复杂性,并且它使得对单独编译的接口的善意的、看起来无害的更改变得太容易破坏实现类的预期语义。

回答by jarnbjo

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.Objectwould 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 在此邮件列表帖子中提供了有关设计决策的更多详细信息。

回答by Marwin

I do not see into the head of Java language authors, so we may only guess. But I see many reasons and agree with them absolutely in this issue.

我看不到 Java 语言作者的头脑,所以我们只能猜测。但在这个问题上,我看到了很多原因并且完全同意它们。

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. Simply put, default methods were designed to provide the defaultbehavior where there is no other definition. Not to provide implementations that will "compete" with other existing implementations.

这些都不适用于 toString 和 Object 的其他方法。简而言之,默认方法旨在提供没有其他定义的默认行为。不提供将与其他现有实现“竞争”的实现。

The "base class always wins" rule has its solid reasons, too. It is supposed that classes define realimplementations, while interfaces define defaultimplementations, which are somewhat weaker.

“基类总是获胜”规则也有其充分的理由。假设类定义了真正的实现,而接口定义了默认实现,这有点弱。

Also, introducing ANY exceptions to general rules cause unnecessary complexity and raise other questions. 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.

总而言之,您提出的解决方案可能会带来比优点更多的缺点。

回答by Kumar Abhishek

The reasoning is very simple, it is because Object is the base class for all the Java classes. 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. That is why to avoid confusion, we cannot have default methods that are overriding Object class methods.

道理很简单,因为 Object 是所有 Java 类的基类。因此,即使我们在某些接口中将 Object 的方法定义为默认方法,它也将毫无用处,因为始终会使用 Object 的方法。这就是为什么为了避免混淆,我们不能有覆盖 Object 类方法的默认方法。

回答by jaco0646

To give a very pedantic answer, it is only forbidden to define a defaultmethod for a publicmethod from java.lang.Object. There are 11 methods to consider, which can be categorized in three ways to answer this question.

举个很迂腐的答案,它只是禁止定义default的方法公开的方法java.lang.Object。有 11 种方法可供考虑,可以分为三种方式来回答这个问题。

  1. Six of the Objectmethods cannot have defaultmethods because they are finaland cannot be overridden at all: getClass(), notify(), notifyAll(), wait(), wait(long), and wait(long, int).
  2. Three of the Objectmethods cannot have defaultmethods for the reasons given above by Brian Goetz: equals(Object), hashCode(), and toString().
  3. Two of the Objectmethods canhave defaultmethods, though the value of such defaults is questionable at best: clone()and 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();
            }
        }
    }
    
  1. 六个Object方法不能有default方法,因为他们final和所有不能被覆盖:getClass()notify()notifyAll()wait()wait(long),和wait(long, int)
  2. 三种Object方法都没有default通过布赖恩戈茨由于上述原因的方法:equals(Object)hashCode(),和toString()
  3. 其中两个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();
            }
        }
    }