当前位置: 首页 > 知识库问答 >
问题:

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

齐承运
2023-03-14

默认方法是Java工具箱中一个很好的新工具。然而,我试图编写一个接口,定义toString方法的default版本。Java告诉我这是禁止的,因为方法是在Java中声明的。lang.Object可能不是默认值。为什么会是这种情况?

我知道有“基类总是赢”的规则,所以默认情况下(双关语;),对象方法的任何默认实现都将被对象中的方法覆盖。但是,我看不出规范中的Object中的方法不应该有例外的原因。特别是对于toString来说,拥有一个默认实现可能非常有用。

那么,Java设计人员决定不允许default方法重写Object中的方法的原因是什么呢?

共有3个答案

赵雪峰
2023-03-14

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

引入默认方法的主要原因是能够向接口添加新方法,而不会破坏旧实现的向后兼容性。默认方法也可用于提供“方便”方法,而无需在每个实现类中定义它们。

这些都不适用于toString和对象的其他方法。简单地说,默认方法被设计为在没有其他定义的情况下提供默认行为。不提供将与其他现有实现“竞争”的实现。

“基类永远获胜”规则也有其坚实的理由。人们认为类定义了真正的实现,而接口定义了默认的实现,而默认的实现稍微弱一些。

此外,对一般规则引入任何例外都会造成不必要的复杂性,并引发其他问题。对象(或多或少)是一个类,那么为什么它应该有不同的行为呢?

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

葛书
2023-03-14

禁止在接口中为java中的方法定义默认方法。lang.Object,因为默认方法永远不会“可访问”。

可以在实现接口的类中覆盖默认接口方法,并且该方法的类实现比接口实现具有更高的优先级,即使该方法是在超类中实现的。因为所有类都继承自java。lang.Object,java中的方法。lang.Object将优先于接口中的默认方法,并将被调用。

Oracle的Brian Goetz在这篇邮件列表文章中提供了更多关于设计决策的细节。

袁康裕
2023-03-14

这是另一个看起来“显然是个好主意”的语言设计问题,直到你开始挖掘,你意识到这实际上是个坏主意。

这封邮件有很多关于这个主题(还有其他主题)有几种设计力量汇聚在一起,将我们带入当前的设计:

  • 保持继承模型简单的愿望;
  • 事实上,一旦你看过去明显的例子(例如,将AbstractList变成一个接口),你会意识到继承equals/hashCode/toString与单个继承和状态紧密相连,接口是多重继承和无状态的;
  • 这可能为一些令人惊讶的行为打开了大门。

你已经触及了“保持简单”的目标;继承和冲突解决规则设计得非常简单(类战胜接口,派生接口战胜超级接口,任何其他冲突都由实现类解决。)当然,这些规则可能会被调整成一个例外,但我认为当你开始使用这个字符串时,你会发现增加的复杂性并不像你想象的那么小。

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

很容易拉出AbstractList示例;如果我们能够摆脱AbstractList并将行为放入List界面中,那将是一件好事。但是,一旦你超越了这个明显的例子,就再也找不到其他好的例子了。在根目录下,AbstractList是为单一继承而设计的。但是接口必须为多重继承而设计。

进一步,想象你正在写这个类:

class Foo implements com.libraryA.Bar, com.libraryB.Moo { 
    // Implementation of Foo, that does NOT override equals
}

Foo编写器查看超类型,没有看到equals的实现,并得出结论,要获得引用相等,他所需要做的就是从Object继承equals。然后,下周,Bar的库维护者“有益地”添加了一个默认的等于实现。哎呀!现在,Foo的语义学已经被另一个维护域中的接口打破,该接口“有益地”为一个通用方法添加了默认值。

默认值应该是默认值。将默认值添加到没有(层次结构中的任何位置)的接口不应影响具体实现类的语义。但如果默认值可以“覆盖”对象方法,那就不是真的。

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

 类似资料:
  • 问题内容: 默认方法是Java工具箱中一个不错的新工具。但是,我试图编写一个定义方法版本的接口。Java告诉我,这是禁止的,因为in中声明的方法可能无法编辑。为什么会这样呢? 我知道有一个“基类始终获胜”规则,因此默认情况下(pun;),方法的任何实现都会被该方法覆盖。但是,我认为没有理由为什么规范中的方法不应有例外。特别是因为它具有默认实现可能非常有用。 那么,Java设计者决定不允许方法覆盖方

  • 我们举个例子: 一个常见的解决方案是转移到一个抽象类,但是在我的具体案例中,我有一个枚举的接口,所以在这里不适用。我想这不是被忽略了,就是因为接口背后的原始想法,即它们是可用方法的“契约”,但我想我需要关于这是怎么回事的输入。 我读过“为什么Java 8接口方法中不允许使用”final“?”,其中说: 默认方法的基本思想是:它是具有默认实现的接口方法,派生类可以提供更具体的实现 与关联问题一样,由

  • 主要内容:1 Java8 默认方法的介绍,2 Java8 默认方法的案例1 Java8 默认方法的介绍 Java提供了一种在接口内部创建默认方法的功能。在接口内部定义并带有默认标记的方法称为默认方法。这些方法是非抽象方法。 2 Java8 默认方法的案例 在下面的示例中,Sayable是一个功能接口,其中包含默认值和抽象方法。默认方法的概念用于定义具有默认实现的方法。您还可以覆盖默认方法,以为该方法提供更具体的实现。 输出结果为:  

  • 学习Java8默认方法。这个链接就像互联网上的其他资源一样说 在“最严格的意义上”,默认方法是一种倒退,因为它们允许你用代码“污染”你的接口。但它们提供了允许向后兼容的最优雅和最实用的方法。它使Oracle更容易更新所有集合类,也使您更容易为Lambda修改现有代码。 我的理解是Java8Dev/Designers在接口中提供了默认方法,这样所有实现类就不必不必要地重写相同的行为,从而提供了向后兼

  • 在Java8中使用缺省方法作为穷人版本的traits是一种安全的做法吗? 有人说,如果你只是为了熊猫而使用它们,可能会让熊猫伤心,因为它很酷,但这不是我的本意。人们还经常提醒说,引入缺省方法是为了支持API演进和向后兼容性,这是真的,但这并不使使用它们作为特征本身是错误的或扭曲的。 我脑海中有以下实际用例: 或者,定义: 关于SO的几个问题与Java vs Scala特性有关;这不是重点。我也不仅

  • 为什么上面的代码有编译错误“java:default method equals in interface Table重写java.lang.Object的一个成员”?难道我们不能使用接口默认方法重写hashCode和equals方法吗?假设我在同一个接口中有方法来确定实现这个接口的对象的相等性?