鉴于此代码:
class Foo {
Integer attr;
public Integer getAttr() {return attr;}
}
List<Foo> list = new ArrayList<>();
list.add(new Foo());
list.stream().map(Foo::getAttr).findAny().orElse(null); //A
list.stream().findAny().map(Foo::getAttr).orElse(null); //B
A行抛出
java.lang.NullPointerException:null
而B行返回null。
这种行为的原因是什么? findAny()
和map()
都返回Optional<T>
。
答案 0 :(得分:6)
list.stream().map(Foo::getAttr).findAny().orElse(null);
Java doc for streams表示Stream:&#34;返回一个流,该流包含将给定函数应用于此流的元素的结果&#34;,而findAny()&#34;可能返回aNullPointerException - 如果选中的元素为null&#34;。在类Foo中,默认情况下,Integer(非int)设置为null,因为已声明但未初始化。请参阅Primitives see default values和Object initialization in Java
初始化不同于: A)类成员(对象和原语) B)局部变量
答案 1 :(得分:4)
显然,由于您执行这些操作的顺序以及findAny
明确说明:throws NullPointerException if the element selected is null
当您执行map(Foo::getAttr)
时,您已有效地将其映射到null
,因此您的Stream现在包含null
;因此findAny
打破了一个异常(因为findAny
应用于该空)
其他操作首先找到Foo
对象,然后将其映射到Foo::getAttr
(从而将其映射到Optional.empty()
),从而调用orElse
。
此外,这对我来说更有意义(至少对我而言):
list.stream()
.findAny()
.flatMap(f -> Optional.ofNullable(f.getAttr()))
.orElse(null);
flatMap
将映射到Optional<Integer>
(属性),以防此empty
获得orElse
结果。
答案 2 :(得分:3)
首先,您的两个代码段map
是不同的操作:
// v--- stream intermediate operation
list.stream().map(Foo::getAttr).findAny().orElse(null); //A
// v---- a Optional utility method
list.stream().findAny().map(Foo::getAttr).orElse(null); //B
并且NullPointerException
出现在Stream#findAny操作中,因为它无法接受null
值。由于它使用Optional.of而不是Optional.ofNullable。 Stream#findAny的文档已经断言:
<强>抛出强>:
NullPointerException
- 如果所选元素为 null
因此,如果您希望A
代码段工作正常,则必须在调用Stream#findAny之前过滤所有null
值,例如:
//when no elements in stream, `findAny` will be return a empty by Optional.empty()
// v
list.stream().map(Foo::getAttr).filter(Objects::nonNull).findAny().orElse(null);//A
答案 3 :(得分:0)
list.stream().map(Foo::getAttr)
...返回一个包含一个元素的流,其值为null。
findAny()
(和findFirst()
)的JavaDoc说:
退货:
一个可选描述此流的某个元素,或者 empty如果流为空,则为可选
投掷:
NullPointerException - 如果选择的元素为null
所以findAny()
完全按照文档记录进行操作:它选择了一个null,因此抛出NullPointerException
。
这是有道理的,因为Optional
是(再次根据JavaDoc,但强调我的):
容器对象,可能包含也可能不包含非null 值
...这意味着您可以保证Optional.ifPresent( x -> x.method())
永远不会因NullPointerException
为空而抛出x
。
因此findAny()
无法返回Optional.of(null)
。而Optional.empty()
表示流是空的,而不是它找到了空。
Stream
/ Optional
基础架构的许多部分都是为了阻止使用空值。
您可以通过将空值映射到Optionals
来产生Optional<Optional<Foo>>
- 这看起来有点复杂,但它是您域的准确表示。 Optional.empty()
表示流为空。 Optional.of(Optional.empty())
表示找到一个空元素:
list.stream().map(Foo::getAttr).map(Optional::ofNullable).findAny()