We can now avoid the NullPointerException in Java with Stream.ofNullable! This post demonstrates how to avoid NullPointerException when working with Java Stream API in Java 9.
Java 8 – The Problem With Stream.of Methods
The Stream interface has the following two methods in Java 8. The problem with these methods is that they throw exceptions when they argument are null or contain null values.
Consider the following sample codes.
Passing A null Array Reference
When we pass null to a non-null-safe overloaded method that takes an array, the codes throw a NullPointerException.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package com.turreta.springboot.java9.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import java.util.stream.Stream; @SpringBootApplication public class ComTurretaSpringbootJava9StreamDemoApplication { public static void main(String[] args) { SpringApplication.run(ComTurretaSpringbootJava9StreamDemoApplication.class, args); String[] arrays = null; // assume returned from some method System.out.println(Stream.of(arrays).count()); } } |
The codes generate the following output.
1 2 3 4 5 6 7 8 9 | Exception in thread "main" java.lang.NullPointerException at java.util.Arrays.stream(Arrays.java:5004) at java.util.stream.Stream.of(Stream.java:1000) at com.turreta.springboot.java9.demo.ComTurretaSpringbootJava9StreamDemoApplication.main(ComTurretaSpringbootJava9StreamDemoApplication.java:16) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) |
Passing A null Reference
In the examples below, we can pass an array that has a null reference element. The array has two elements – “a” and null. When the codes process that null element, the codes throw a NullPointerException. Consider the following codes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; public class DemoStreamOf02 { public static void main(String[] args) { String[] arr = {"a", null}; System.out.println(Stream.of(arr).filter( s->s.equals("a") ).count()); } } |
This results in:
1 2 3 4 5 6 7 8 9 10 11 12 | Exception in thread "main" java.lang.NullPointerException at DemoStreamOf02.lambda$main$0(DemoStreamOf02.java:14) at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:176) at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) at java.base/java.util.stream.ReduceOps$5.evaluateSequential(ReduceOps.java:257) at java.base/java.util.stream.ReduceOps$5.evaluateSequential(ReduceOps.java:248) at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.base/java.util.stream.ReferencePipeline.count(ReferencePipeline.java:538) at DemoStreamOf02.main(DemoStreamOf02.java:15) |
Consider another example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; public class DemoStreamOf01 { public static void main(String[] args) { String tmp = null; System.out.println(Stream.of(tmp).filter( s->s.equals("a") ).count()); } } |
This results in a similar output:
1 2 3 4 5 6 7 8 9 10 11 12 | Exception in thread "main" java.lang.NullPointerException at DemoStreamOf01.lambda$main$0(DemoStreamOf01.java:14) at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:176) at java.base/java.util.stream.Streams$StreamBuilderImpl.forEachRemaining(Streams.java:411) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) at java.base/java.util.stream.ReduceOps$5.evaluateSequential(ReduceOps.java:257) at java.base/java.util.stream.ReduceOps$5.evaluateSequential(ReduceOps.java:248) at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.base/java.util.stream.ReferencePipeline.count(ReferencePipeline.java:538) at DemoStreamOf01.main(DemoStreamOf01.java:15) |
In Java 8, the only way to avoid the NullPointerException is by using if statements to check for null values.
Avoid NullPointerException With Java 9 Stream.ofNullable
This new method returns an empty stream is the passing reference is null.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; public class DemoUsingStreamOfNullable { public static void main(String[] args) { String tmp = null; System.out.println(Stream.ofNullable(tmp).filter( s->s.equals("a") ).count()); } } |
This outputs: 0
As easy as that! We can now avoid NullPointerException with Stream.ofNullable when using Java Stream API in Java 9.
Although the Java Stream API is powerful, it is generally to keep its usage to a minimum. Codebase with a lot of Lamda expressions is large to analyze because of poor readability. On the other hand, when we use Java Stream API we need to be aware that they are not bullet-proof and they can throw NullPointerException. Now, a couple NullPointerException with too many hard-to-read Lambda expressions, our codes will be hard to maintain. Using Stream.ofNullable can ease the pain but it is just a nice-to-have feature in real-life application codes.
So the takeaways are – avoid the Java Stream API as much as possible, and, if we cannot, make sure we are using the Stream.ofNullable method or use if statements for checking for null values.
This post is (now) part of a reboot Java tutorial.