我应该对每个可能返回 null 的方法使用 Java8/Guava Optional 吗?

2022-08-31 15:55:59

Optional 用于表示可为 null 的对象,此类的某些用法包括

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

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


答案 1

那么可选有什么问题呢?

我们面临的问题是:JDK 8 可选对象会摆脱空引用吗?答案是肯定的否定!因此,批评者立即质疑它的价值,问道:那么我们用其他方式无法做到的有什么好处呢?

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

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

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

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

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

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

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

“除了通过为null命名来提高可读性之外,Optional的最大优势是它的白痴证明性。如果您希望程序进行编译,它会迫使您积极考虑不存在的情况,因为您必须主动解开Optimed并解决这种情况”。

所以,我想这取决于每个API设计人员在使用Anyport时想要走多远。

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


答案 2

与番石榴相比,一个令人讨厌的问题是它没有提供像java.util.Optional

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

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

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

缺乏这个特定的功能限制了Java 8的Anstable的monadic应用程序。

更新:

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

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

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

更新:

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

optionalA.or(() -> computeOptionalB)

这很整洁!


推荐