我已经使用Java 8 6个多月了,我对新的API变化非常满意。我仍然不确定的一个领域是什么时候使用Optional。我似乎在想要在任何地方使用它之间摇摆,有些东西可能是空的,而根本没有。

似乎在很多情况下我都可以使用它,但我不确定它是否会增加好处(可读性/零安全性),还是只会导致额外的开销。

所以,我有几个例子,我对社区对Optional是否有益的想法很感兴趣。

1 -当方法可以返回null时,作为一个公共方法返回类型:

public Optional<Foo> findFoo(String id);

2 -当参数可以为空时,作为方法参数:

public Foo doSomething(String id, Optional<Bar> barOptional);

3 -作为bean的可选成员:

public class Book {

  private List<Pages> pages;
  private Optional<Index> index;

}

4 -收集:

总的来说,我不认为:

List<Optional<Foo>>

添加任何东西-特别是因为一个人可以使用过滤器()删除空值等,但在集合中有任何可选的好用途吗?

有我错过的案子吗?


当前回答

Optional的主要设计目标是为函数返回值提供一种方法,以指示没有返回值。请看这个讨论。这允许调用方继续一连串流畅的方法调用。

这与OP问题中的用例#1最接近。尽管,缺少值是比null更精确的表述,因为像IntStream。findFirst永远不能返回null。


对于用例#2,将一个可选参数传递给一个方法,这可以实现,但相当笨拙。假设您有一个方法,它接受一个字符串,后面跟着一个可选的第二个字符串。接受Optional作为第二个参数将导致如下代码:

foo("bar", Optional.of("baz"));
foo("bar", Optional.empty());

即使接受null也更好:

foo("bar", "baz");
foo("bar", null);

最好的方法是有一个重载方法,接受单个字符串参数,并为第二个参数提供默认值:

foo("bar", "baz");
foo("bar");

这确实有局限性,但它比上面任何一种都要好得多。

用例#3和#4,在类字段或数据结构中使用Optional,被认为是对API的滥用。首先,它违背了上文所述的Optional的主要设计目标。其次,它不会增加任何价值。

处理Optional中缺少值的方法有三种:提供替代值、调用函数提供替代值或抛出异常。如果你要存储到一个字段中,你会在初始化或赋值时这样做。如果您正在向列表中添加值,如OP所述,您可以选择不添加值,从而“平坦化”缺少的值。

我相信有人会想出一些人为的例子,他们确实想在字段或集合中存储Optional,但通常情况下,最好避免这样做。

其他回答

Oracle教程:

Optional的目的不是替换代码库中的每一个空引用,而是帮助设计更好的api,使用户可以通过读取方法的签名来判断是否需要一个可选值。此外,Optional强制您主动展开Optional以处理缺少值的情况;因此,可以保护代码不受意外空指针异常的影响。

我不认为Optional是可能返回空值的方法的一般替代品。

基本思想是:一个值的缺失并不意味着它在未来可能是可用的。它是findById(-1)和findById(67)之间的差值。

可选项对于调用者的主要信息是,他可能不指望给定的值,但它可能在某个时间可用。也许它会再次消失,然后再回来一次。这就像一个开/关开关。你可以“选择”打开或关闭灯。但是如果你没有灯可以打开,你就别无选择了。

所以我发现它太乱引入可选的地方,以前可能返回null。我仍然会使用null,但是只在一些受限的地方,比如树的根、惰性初始化和显式查找方法。

Optional的主要设计目标是为函数返回值提供一种方法,以指示没有返回值。请看这个讨论。这允许调用方继续一连串流畅的方法调用。

这与OP问题中的用例#1最接近。尽管,缺少值是比null更精确的表述,因为像IntStream。findFirst永远不能返回null。


对于用例#2,将一个可选参数传递给一个方法,这可以实现,但相当笨拙。假设您有一个方法,它接受一个字符串,后面跟着一个可选的第二个字符串。接受Optional作为第二个参数将导致如下代码:

foo("bar", Optional.of("baz"));
foo("bar", Optional.empty());

即使接受null也更好:

foo("bar", "baz");
foo("bar", null);

最好的方法是有一个重载方法,接受单个字符串参数,并为第二个参数提供默认值:

foo("bar", "baz");
foo("bar");

这确实有局限性,但它比上面任何一种都要好得多。

用例#3和#4,在类字段或数据结构中使用Optional,被认为是对API的滥用。首先,它违背了上文所述的Optional的主要设计目标。其次,它不会增加任何价值。

处理Optional中缺少值的方法有三种:提供替代值、调用函数提供替代值或抛出异常。如果你要存储到一个字段中,你会在初始化或赋值时这样做。如果您正在向列表中添加值,如OP所述,您可以选择不添加值,从而“平坦化”缺少的值。

我相信有人会想出一些人为的例子,他们确实想在字段或集合中存储Optional,但通常情况下,最好避免这样做。

似乎Optional只有在Optional中的类型T是int、long、char等基本类型时才有用。对于“真正的”类,这对我来说没有意义,因为你可以使用空值。

我认为它是从这里(或从另一个类似的语言概念)。

Nullable < T >

在c#中,这个Nullable<T>很久以前就被引入到换行值类型中。

在java中,除非你沉迷于函数式编程,否则不要使用它们。

它们没有作为方法参数的位置(我保证有一天会有人传递给你一个空的可选参数,而不仅仅是一个空的可选参数)。

它们对返回值有意义,但它们会让客户端类继续延伸行为构建链。

FP和链在像java这样的命令式语言中几乎没有位置,因为它使调试变得非常困难,而不仅仅是阅读。当你走到这一行时,你无法知道程序的状态和意图;你必须一步一步地找出它(尽管有步骤过滤器,但通常不是你的代码和许多堆栈帧深),你必须添加大量断点,以确保它可以停止在你添加的code/lambda中,而不是简单地走if/else/call琐碎的行。

如果你想要函数式编程,选择java以外的语言,并希望你有调试它的工具。