【问题标题】:Should I use Java8/Guava Optional for every method that may return null?我应该为每个可能返回 null 的方法使用 Java8/Guava Optional 吗?
【发布时间】:2013-09-11 22:32:27
【问题描述】:

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

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

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

【问题讨论】:

标签: java guava optional java-8


【解决方案1】:

那么 Optional 有什么问题?

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

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

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

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

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

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

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

引用Google Guava's article关于可选对象的使用:

“除了给 null a 带来的可读性增加 顾名思义,Optional 的最大优点是它的 idiot-proof-ness。它 如果你想要你的 程序完全编译,因为你必须主动解开 可选并解决这种情况”。

所以,我想每个 API 设计人员都可以选择他们希望在 Optional 的使用方面走多远。

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

【讨论】:

  • 当你说用户被强制时,API 来验证......你能告诉你在 java 8 中是如何发生的......因为用户可以调用 get() ,这会导致异常抛出
  • 我认为Optional API最大的错误是暴露了一个可以抛出NoSuchElementException的“get”方法。 Optional 应始终通过“orElse”或“orElseGet”方法展开,以便始终可以返回值,最好是非空值。如果我们不想解开值,总是有允许检查的“isPresent”方法。在我的工作中,我总是将 Optional 与 map、flatMap、filter lambda 方法一起使用。
【解决方案2】:

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

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

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

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

缺乏这一特定功能限制了 Java 8 的 Optional 的一元应用。

更新:

Guava 的 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)

这很好!

【讨论】:

  • @JeffreyBosboom,opt.flatMap(fn) 等效于 opt.isPresent()?fn.apply(opt.value):opt,而 opt.orElse(opt2) 等效于 opt.isPresent()?opt:opt2。你能看出区别吗?
  • T orElse(T other)中有java.util.Optionaldocs.oracle.com/javase/8/docs/api/java/util/…
  • @AdamSiemion,orElse(T) 产生 T,而 orElse(Optional) 产生 Optional。你能看出两者的区别吗?
  • Java 团队很高兴意识到 Guava Optional 很有用,并将其添加到 Java 8 中,同时确保两者略有不兼容!
【解决方案3】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-11
    • 2018-07-04
    • 1970-01-01
    • 2014-08-05
    • 1970-01-01
    • 1970-01-01
    • 2015-10-07
    • 2010-12-29
    相关资源
    最近更新 更多