62

Optional 用于表示可为空的对象,该类的一些用途包括

  1. 作为方法返回类型,作为返回 null
    表示没有可用值的替代方法
  2. 区分“未知”(例如,不存在于地图中)和“已知没有价值”(存在于地图中,具有值
    Optional.absent())
  3. 将可空引用包装在不支持 null 的集合中进行存储(尽管应该首先考虑其他几种方法)

对于第一种情况,我需要在所有可为空的返回方法中返回 Optional 吗?

4

3 回答 3

75

那么可选有什么问题呢?

我们面临的问题是:JDK 8 Optional 对象会摆脱空引用吗?答案是明确的不!所以,批评者立即质疑它的价值:那么它有什么好处是我们已经无法通过其他方式做到的?

与从未有空引用概念的 SML 或 Haskell 等函数式语言不同,在 Java 中我们不能简单地摆脱历史上存在的空引用。这将继续存在,并且可以说它们有适当的用途(仅举一个例子:三值逻辑)。

我怀疑 Optional 类的目的是替换每个可空引用,而是帮助创建更健壮的 API,在这些 API 中,只需读取方法的签名,我们就可以判断我们是否可以期望一个可选值,并且强制程序员相应地使用这个值。但归根结底,Optional 将只是另一个引用,并且受到该语言中所有其他引用的相同弱点(例如,您可以返回 null Optional)。很明显,Optional 不会挽救这一天。

这些可选对象应该如何使用,或者它们在 Java 中是否有价值,一直是项目 lambda 邮件列表中激烈争论的问题。我们从批评者那里听到有趣的论点,例如:

  • 存在其他替代方案的事实(例如,像 IntelliJ 和 Eclipse IDE 这样的 IDES 支持一组专有注释,用于对可空性进行静态分析,带有 @Nullable 和 @NonNull 等注释的JSR-305 )。
  • 有些人希望它可以在函数式世界中使用,这在 Java 中并不完全可行,因为该语言缺少函数式编程语言(如 SML 或 Haskell)中存在的许多特性(例如模式匹配)。
  • 其他人则争论如何改造现有代码以使用此惯用语是不可能的(例如,List.get(Object) 将继续返回 null)。
  • 有些人抱怨缺乏对可选值的语言支持,这可能会导致在 API 中不一致地使用Optional 的情况,这会造成不兼容,就像我们将与 Java API 的其余部分一样不能改装为使用新的 Optional 类。这几乎是你的问题。在支持可选类型的语言中,例如 CeylonKotlin 中,您甚至不会质疑这一点。
  • 一个令人信服的论点是,如果程序员在可选对象中调用 get 方法,如果它是空的,它将引发 NoSuchElementException,这与我们遇到的 null 问题几乎相同,只是有一个不同的异常。

因此,看来 Optional 的好处确实值得怀疑,并且可能仅限于提高可读性和执行公共接口合同。

我确实相信,采用这种可选的函数式惯用语可能会使我们的代码更安全,减少空解引用问题的提示,因此更健壮,更不容易出错。当然,这不是一个完美的解决方案,因为毕竟,可选引用也可能被错误地设置为空引用,但我希望程序员坚持不传递空引用的约定,而不是在期望可选对象的地方传递空引用,几乎就像我们今天考虑一个好习惯,不要在期望集合或数组的地方传递空引用,在这些情况下,正确的是传递空数组或集合。这里的要点是,现在我们在 API 中有一个机制,我们可以使用它来明确说明对于给定的引用,我们可能没有分配值,并且 API 强制用户验证这一点。

引用Google Guava关于使用可选对象的文章:

“除了通过给 null 命名来增加可读性之外,Optional 的最大优势是它的白痴性。如果您希望程序完全编译,它会迫使您积极考虑缺席的情况,因为您必须主动解开 Optional 并解决这种情况”。

所以,我想每个 API 设计者都可以选择他们想在使用 Optional 时走多远。

一些有影响力的开发人员,如 Stephen Colebourne 和 Brian Goetz 最近发表了一些关于正确使用可选的有趣文章。我发现以下内容特别有用:

于 2013-09-09T13:32:49.113 回答
8

与 Guava 相比,一个令人讨厌的问题java.util.Optional是它没有提供类似的方法

orElse(Optional<T>): Optional<T>

另一方面,定义com.google.common.base.Optional

or(Optional<T>): Optional<T>

缺少此特定功能限制了 Java 8 的 Optional 的单子应用程序。

更新:

番石榴or(Optional<T>)可以像在 Java 8 中一样被复制

optionalA.map(Optional::of).orElse(optionalB)

或者

optionalA.map(Optional::of).orElseGet(() -> computeOptionalB)

更新:

Java 9(终于!)中,您将能够使用Optional.or(Supplier<Optional<T>>)

optionalA.or(() -> computeOptionalB)

那很整齐!

于 2014-10-19T01:24:38.883 回答
8

作为观察,我认为在设计应用程序时必须考虑的最重要方面之一是决定如何处理“空问题”。在这方面,第一步是确定可能的空值“来源”。例如,项目中使用的数据库或外部库。

下一步将是“包含”问题,即包装有问题的代码(使用 Optional),从而阻止 null 在整个系统中传播,其中毫无戒心的代码可能会触发 NPE。

要回答你的问题,这取决于......大多数时候我会说是,特别是对于存在于应用程序中不同“关注点”(或层)的薄边界的方法,(接口/类的签名例如,您用于查询数据存储区或用于“传输”可能具有空属性的数据的 pojo(DTO,或者更一般的描述,不同模块的已发布API)应避免将空值“泄漏”到其他一些区域不同的担忧。

于 2013-09-09T16:12:07.940 回答