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

时间:2013-09-08 06:52:51

标签: java guava optional java-8

可选用于表示可为空的对象,此类的一些用法包括

  1. 作为方法返回类型,作为将null返回到
    的替代方法 表示没有可用的值
  2. 区分“未知”(例如,地图中不存在) 并且“已知没有价值”(出现在地图中,值为
    Optional.absent())
  3. 将可存储的引用包装在一个不存储的集合中 支持null(虽然有其他几种方法可以解决这个问题 应该首先考虑)
  4. 对于第一种情况,我是否需要在所有可空的返回方法中返回Optional?

3 个答案:

答案 0 :(得分:75)

那么可选的错误是什么?

我们面临的问题是:JDK 8可选对象是否会删除空引用?答案是强调不!因此,批评者立即质疑其价值问题:那么我们还不能通过其他方式做什么有益呢?

与SML或Haskell之类的函数式语言不同,它们从未有过空引用的概念,在Java中我们不能简单地去除历史上存在的空引用。这将继续存在,并且可以说它们具有正确的用途(仅举一例:three-valued logic)。

我怀疑Optional类的意图是替换每一个可以为空的引用,但是为了帮助创建更强大的API,只需通过读取方法的签名,我们可以告诉我们是否可以期望一个可选值与否,并强制程序员相应地使用此值。但最终,Optional将只是另一个引用,并且受到语言中每个其他引用的相同弱点(例如,您可以返回null可选)。很明显,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可能是used inconsistently,这会产生不兼容性,就像我们将要使用的那样Java API的其余部分无法改装以使用新的Optional类。这几乎是你的问题。在支持可选类型like in Ceylonlike in Kotlin的语言中,您甚至不会质疑这一点。
  • 一个令人信服的论点是,如果程序员在一个可选对象中调用get方法,如果它是空的,它将引发NoSuchElementException,这与我们对null的问题几乎相同,只是有一个不同的异常。

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

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

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

  

“除了给出null之外的可读性的增加   名称,Optional的最大优点是它的白痴证明。它   如果你想要你的话,强迫你积极思考缺席的情况   程序要编译,因为你必须积极打开   可选并解决该案例“。

所以,我想每个API设计师都可以选择使用Optional的目的。

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

答案 1 :(得分:8)

作为观察,我认为在构建应用程序时必须考虑的一个最重要的方面是决定如何处理“空问题”。 在这方面,第一步是确定空值的可能“来源”。例如,项目中使用的数据库或外部库。下一步是“包含”问题,即包装有问题的代码(使用Optional),从而阻止在整个系统中传播null,其中毫无疑问的代码可能会触发NPE。
要回答你的问题,这取决于...大多数时候我会说这是不必要的,因为它创造了许多没有价值的工作(例如,我不会对在类中调用其他私有方法的方法使用可选项,或者调用包私有类方法的方法,但是应用程序中不同“关注点”(或层)的瘦边界存在的代码,(接口/类的签名)例如,您用于查询数据存储区,或用于“传输”可能具有空属性的数据的pojos - DTO,或更一般的描述,已发布的不同模块的API)应该避免'泄漏'对其他一些有不同顾虑的领域是空的。

答案 2 :(得分:7)

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

这样的方法

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

另一方面,

com.google.common.base.Optional中定义为

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

缺少此特定功能限制了Java 8的可选的monadic应用程序。

<强>更新

可以像使用Java 8一样复制Guava的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)

那很整洁!