Recently, while working with Java 8 streams, I came across a NullPointerException on a reduce operation while working with the following test cases:
private static final BinaryOperator<Integer> sum = (a, b) -> {
if (a == null) return b;
if (b == null) return a;
return Integer.sum(a, b);
};
List<Integer> s = new ArrayList<>();
s.add(null);
s.add(null);
s.add(null);
Integer i = s.stream().reduce(sum).orElse(null);
// throws NPE
Integer i = s.stream().reduce(sum).orElse(2);
// throws NPE
Integer i = s.stream().reduce(null,(a, b)->null);
// returns a value i.e null
Or alternatively:
Integer i = s.stream().filter(Objects::nonNull).reduce(Integer::sum).orElse(null);
// returns a value i.e null
Upon checking the reduce operation, I came across this class which performs the reduce operation:
class ReducingSink implements AccumulatingSink<T, Optional<T>, ReducingSink> {
private boolean empty;
private T state;
public void begin(long size) {
empty = true;
state = null;
}
@Override
public void accept(T t) {
if (empty) {
empty = false;
state = t;
} else {
state = operator.apply(state, t);
}
}
@Override
public Optional<T> get() {
return empty ? Optional.empty() : Optional.of(state);
}
@Override
public void combine(ReducingSink other) {
if (!other.empty)
accept(other.state);
}
}
In the above code, you see that the get() method returns an optional value if the boolean empty is false, and in my case the value is false but state is null, so Optional.of(null) throws a NullPointerException. In my case I have a binary operator which allows null.
So I think the code
return empty ? Optional.empty() : Optional.of(state);
should be changed to
return empty || state == null ? Optional.empty() : Optional.of(state);
As my binary operator (which has the task of reducing) and is okay with null.