是否可以在不破坏 Java 的情况下向 Comparable 添加默认方法?

Posted

技术标签:

【中文标题】是否可以在不破坏 Java 的情况下向 Comparable 添加默认方法?【英文标题】:Would it be possible to add default methods to Comparable without breaking Java? 【发布时间】:2021-04-28 04:25:30 【问题描述】:

我正在考虑提出一个添加默认方法的功能请求:

default boolean greaterThan(T o) 
    return compareTo(o) > 0;


default boolean smallerThan(T o) 
    return compareTo(o) < 0;


default boolean atLeast(T o) 
    return compareTo(o) >= 0;


default boolean atMost(T o) 
    return compareTo(o) <= 0;

Comparable 接口,因为它会使代码更具可读性 - 在我看来。

但是,我想知道这是否会破坏代码。如果我们添加默认方法,是否有任何代码(如 Lambda)会中断?

如果我正确阅读了this,我会假设 Lambda 至少会继续工作,只要他们不会尝试调用新方法,但我不确定。而且我不知道是否可能存在任何(明显的)其他问题。

请不要讨论可能的功能请求本身。

【问题讨论】:

可能发生的事情:stuartmarks.wordpress.com/2020/09/22/… 可能会有帮助:docs.oracle.com/javase/specs/jls/se15/html/… 谢谢大家,都阅读了。这是有道理的,它可能会导致问题,但我也不认为它们非常危险。在源代码级别引起命名冲突一定是一种非常奇怪的方法,但是是的,你永远不知道。 @Slaw 为什么不回答这个问题?恕我直言,再加上一个例子,直接回答了。 @Eugene 不确定我是否理解得足够透彻,无法给出答案。 【参考方案1】:

作为commented by Johannes Kuhn、Stuart Marks documented a case,将CharSequence.isEmpty() 默认方法添加到Java 15 会破坏第三方库。

因此,鉴于历史上优先考虑使用现有应用程序最大程度地开发 Java 的新版本 backward-compatible,更好的方法是定义一个新接口。新界面将介绍您的新方法。理论上,旧接口最终可能会在很长一段时间后被标记为弃用,以建议逐步淘汰旧接口以换取新接口。同时,现有代码不会受到干扰,而新代码可以利用新思想。

例如,这是在Map 接口中完成的。子接口 SortedMap 与 Java 1.2(又名 Java 2)一起出现。多年后,在 Java 6 中,出现了用其他方法扩展该接口的想法。但是修改SortedMap 存在扰乱捆绑Java 库中现有实现以及破坏第三方实现的风险。所以他们选择定义一个新的接口NavigableMap。他们将较新的接口NavigableMap 设为SortedMap 的子接口。

NavigableMap 实际上只是SortedMap 的“2.0”版本。据我所知,任何实现都没有理由不想支持更大的方法套件,而不是在SortedMap 中找到的更少的方法。所以,当然,最好只有一个这样的界面而不是两个,但是c'est la vie

这是我制作的图表,显示了与 Java 11 捆绑在一起的 Map 接口的各种实现。您可以看到接口 SortedMapNavigableMap 如何并排运行。

我全心全意地支持您使用这些特定方法有效扩展Comparable 的想法(顺便说一句,命名也不错)。但我相信你的提议只会作为Comparable 的新子接口飞起来。

【讨论】:

谢谢,老实说应该是lessThan。现在为新的Comparable命名... 不过,这个答案中关于 SortedMap 的部分是完全错误的。现有的任何比较器都可以免费获得atMost 方法。这确实是一种默认方法。与 SortedMap 对比:并非所有地图都已排序;像“headMap”这样的方法不能(明智地)以默认方式编写,以便它们适用于所有现有的地图接口。 headMap 所做的任务基本上是低效的,例如HashMap,最常用的地图实现。 默认方法在 Java 6 中不可用,这是直接相关的。 @rzwitserloot 我从来没有说过将SortedMap 应用于所有Map 实现。我的答案是关于SortedMapNavigableMap 之间的关系。请告诉我我在哪里讨论过应用于所有Map 实现,以便我可以修复该部分。 是的,这很不幸。我误读了您的评论,但现在您的回答存在一个不同的基本问题: NavigableMap 是在 default 方法不存在时创建的,这使得 SortedMap/NavigableMap 问题几乎与这个问题完全无关。这个答案仍然具有误导性并得出错误的结论,即使它确实开始链接到有用的资源。【参考方案2】:

不幸的是,@BasilBourque 的回答具有误导性,即使它引用了有用的实体。

无法通过子接口引入此功能。 SortedMap/NavigableMap 适用:default 机制在引入NavigableMap 时还不存在!

此时,将NavigableMap 中的部分或全部方法引入SortedMap 中的default 方法可能是值得的,其实现适用于 sortedmaps,并且最终会自动被任何 Map 覆盖已经实现 NavigableMap 的 impls(因为它们已经覆盖了它们 impl 中的所有这些)。

但是,这意味着“嘿,看看 NavigableMap,它类似于 SortedMap,就像你想对 Comparable 做的一样”是误导性论点:你的提议涉及添加默认方法。那时候还没有这个。

最终,您想要的是“几乎但不完全向后兼容”。 Oracle/Java Lang 架构师倾向于说“java 不会破坏向后兼容性”,但这是一个严重的过度简化和边缘谎言。如果你问得更远一点,他们最终会承认这不是真的:问题的核心是 java 权衡任何向后中断的成本(它的可能性有多大,确实破坏它的代码会发生什么?如果它默默地仍然可以编译和运行,但是做错了,这太可怕了,如果重构脚本每次都可以自动应用的微不足道的更新,那就太好了 - 然后混合任何给定代码库偶然发现它的几率,你就有了一个想法'成本')与它的好处。

这里的成本非常小。任何代码库都极不可能发生故障。但是,如果您想实际提出此功能,那么做一些研究并尝试考虑任何库会很有帮助(或者如果您不能,请查看各种人博客的前 100 个列表,分析例如 github java项目作为源材料)通过一堆来尝试看看.isEmpty发生的事情是否会发生在这些方法中。

然后,有一个坏消息:提出一个 java 特性是极不可能成功的。从某种角度来看,作为 Project Lombok 的作者,他花费数百小时阅读像 lambda-dev 这样的邮件列表,他贡献了大部分实际的 lambda 语法和概念基础,并与多个 Java 语言架构师进行了对话一些长度(主要是在 Java 会议上),我已经发送了一个提案,其中包括 Java 本身的完整补丁,以及 JLS 所需的所有适当更新,它根本没有破坏向后兼容性,而且它从来没有得到任何地方。甚至不如 JEP。

上次我与 Oracle 工程师交谈时,他们确实承诺现在情况会好一些,但这并没有特别远:我不知道周围或即将推出的任何 Java 功能是由一个被直接踢到 JEP 并在过去 10 年中接近 Java 功能可预见范围的局外人。有时有人(通常是 Joe Darcy 或 Alex Buckley)会运行一个项目来“添加一堆方便的小语言更改”,例如 Project Coin(那是 11 年前的事情,oof)。对此的第二个看法是Project Amber,我认为这是一个更连续的项目,而不是具有特定短暂时间跨度的硬币。

将其导入 java 的路径大概会通过 amber。

【讨论】:

再一次,我从来没有说过 SortedMap 应该应用于每个 Map 实现。你以完全错误的解释扭曲了我的回答。请收回。 您的回答从根本上仍然具有误导性,但出于完全不同的原因(是的,我第一次看错了)。答案已被编辑以反映这一点。 老实说,我认为不应该用答案来攻击其他答案;如果您认为事情可以改进,您可以使用 cmets 部分(我猜您已经这样做了)。除此之外,你已经表明你对最初的攻击是错误的,但文本似乎仍然包含它。 @MAartenBodewes 不,文本已更新,论证使用推理和可证伪信息来证明其理由。将其视为对人的攻击-这是在您的脑海中,而不是在此答案中。它绝不会影响 Basil Bourque 的信誉或品格。它只是破坏了他的论点(使用可证伪)。不要在不存在的地方发明个人的轻视。

以上是关于是否可以在不破坏 Java 的情况下向 Comparable 添加默认方法?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以在不启动核心定位的情况下向用户询问当前位置

是否可以在不构建任何应用程序的情况下向 Android 发送推送通知?

如何在不打开 WhatsApp 的情况下向 WhatsApp 群组发送消息?

在客户端上使用 SERVER_API 密钥是不是可以,是不是可以在不使用 SERVER_API 密钥的情况下向设备组发送 GCM 消息?

我可以在不加入 IRC 频道的情况下向它发送消息吗?

我可以在不安装 TestFlight 的情况下向自己发送 iPhone 应用吗