This is a question regarding a possibly confusing Streams behavior. I came under the impression that the .map operaion (because of it's usage in Optional) was always null-safe (I'm aware that these .map's are different implementations thought they share the same name). And I was quite surprised when I got a NPE when I used it so in a (list) stream. Since then, I started using Objects::nonNull with streams (both with .map & .flatMap operations).
Q1. Why is it that Optional can handle nulls at any level, whereas Streams can't (at any level), as shown in my test code below? If this is the sensible and desirable behavior, please give an explanation (as to it's benefits, or the downsides of List Stream behaving like Optional).
Q2. As a follow up, is there an alternative to the excessive null-checks that I perform in the getValues method below (which is what prompted me to think why Streams could not behave like Optional).
In the below test, I'm interested in the innermost class's value field only.
I use Optional in getValue method.
I use Streams on list in getValues method. And I cannot remove a single nonNull check in this case.
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
public class NestedObjectsStreamTest {
@Getter @AllArgsConstructor
private static class A {
private B b;
}
@Getter @AllArgsConstructor
private static class B {
private C c;
}
@Getter @AllArgsConstructor
private static class C {
private D d;
}
@Getter @AllArgsConstructor
private static class D {
private String value;
}
public static void main(String[] args) {
A a0 = new A(new B(new C(new D("a0"))));
A a1 = new A(new B(new C(new D("a1"))));
A a2 = new A(new B(new C(new D(null))));
A a3 = new A(new B(new C(null)));
A a5 = new A(new B(null));
A a6 = new A(null);
A a7 = null;
System.out.println("getValue(a0) = " + getValue(a0));
System.out.println("getValue(a1) = " + getValue(a1));
System.out.println("getValue(a2) = " + getValue(a2));
System.out.println("getValue(a3) = " + getValue(a3));
System.out.println("getValue(a5) = " + getValue(a5));
System.out.println("getValue(a6) = " + getValue(a6));
System.out.println("getValue(a7) = " + getValue(a7));
List<A> aList = Arrays.asList(a0, a1, a2, a3, a5, a6, a7);
System.out.println("getValues(aList) " + getValues(aList));
}
private static String getValue(final A a) {
return Optional.ofNullable(a)
.map(A::getB)
.map(B::getC)
.map(C::getD)
.map(D::getValue)
.orElse("default");
}
private static List<String> getValues(final List<A> aList) {
return aList.stream()
.filter(Objects::nonNull)
.map(A::getB)
.filter(Objects::nonNull)
.map(B::getC)
.filter(Objects::nonNull)
.map(C::getD)
.filter(Objects::nonNull)
.map(D::getValue)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
}
Output
getValue(a0) = a0
getValue(a1) = a1
getValue(a2) = default
getValue(a3) = default
getValue(a5) = default
getValue(a6) = default
getValue(a7) = default
getValues(aList) [a0, a1]