我已经使用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<T>实例上执行的方法:
地图
平面地图
或者其他
orElseThrow
ifPresentOrElse
获取
以下是你可以在null上执行的所有方法:
(没有)
这实际上是一个苹果和橙子的比较:Optional<T>是一个对象的实际实例(除非它是null…但这可能是一个错误),而null是一个中止的对象。对于null,您所能做的就是检查它实际上是否为空。所以如果你喜欢在对象上使用方法,Optional<T>适合你;如果您喜欢在特殊的字面量上进行分支,那么null是适合您的。
Null不构成。您不能简单地组合一个只能进行分支的值。但是Optional<T>确实作曲。
例如,您可以使用map创建任意长的“如果非空则应用此函数”链。或者,您可以有效地使用ifPresent创建一个命令代码块,如果可选值非空,则使用该可选值。或者你可以通过使用ifPresentOrElse来创建一个“if/else”,如果非空则使用非空可选参数,否则执行其他代码。
在这一点上,我们遇到了我认为语言的真正局限性:对于非常命令式的代码,你必须将它们包装在lambdas中,并将它们传递给方法:
opt.ifPresentOrElse(
string -> { // if present...
// ...
}, () -> { // or else...
// ...
}
);
这对某些人来说可能不够好,就风格而言。
如果Optional<T>是一个可以进行模式匹配的代数数据类型(这显然是伪代码:
match (opt) {
Present(str) => {
// ...
}
Empty =>{
// ...
}
}
总之,总结一下:Optional<T>是一个非常健壮的空或现对象。Null只是一个哨兵值。
主观上忽视了原因
There seems to be a few people who effectively argue that efficiency should determine whether one should use Optional<T> or branch on the null sentinel value. That seems a bit like making hard and fast rules on when to make objects rather than primitives in the general case. I think it’s a bit ridiculous to use that as the starting point for this discussion when you’re already working in a language where it’s idiomatic to make objects left-and-right, top to bottom, all the time (in my opinion).
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,但通常情况下,最好避免这样做。