I have some code that is playing with the Java Stream API:
boolean isAuthorized = authorizationByTenant.stream()
.filter(auth -> auth.getTenantName().equalsIgnoreCase(tenant))
.map(auth -> auth.getAuthorizedRoutes().get(component))
.flatMap(Collection::stream)
.anyMatch(routeDefinition -> isMatchingRoute(routePath, routeDefinition));
And I sometimes get an exception pointing to the anyMatch
line:
java.lang.NullPointerException: null
at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:273) ~[na:na]
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) ~[na:na]
at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) ~[na:na]
at java.base/java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1602) ~[na:na]
at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:129) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:527) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na]
at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:230) ~[na:na]
at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:196) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
at java.base/java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:632) ~[na:na]
at com.mycompany.routefilter.functionality.FunctionalityService.isAuthorizedRoute(FunctionalityService.java:55) ~[classes!/:1.0-SNAPSHOT]
at
My question here is what is null? Is it routeDefinition
? And why does it make the call fail? I expect that if routeDefinition
is null my method isMatchingRoute
would return false.
The NullPointerException is occurring in your .flatMap(Collection::stream)
expression.
I created a similar example and expanded the method reference into a lambda. When causing a null value there you will see the stacktrace become more clear.
Example:
Stream.of("foo")
.map(foo -> (List<String>) null)
.flatMap(Collection::stream)
.anyMatch(string -> "gnarly".equals(string)); // LINE 13
Leads to
Exception in thread "main" java.lang.NullPointerException
at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:271)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at java.base/java.util.stream.Streams$StreamBuilderImpl.tryAdvance(Streams.java:397)
at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:127)
at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:502)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:488)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:230)
at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:196)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:528)
at Test.main(Test.java:13)
When I replace it with:
Stream.of("foo")
.map(foo -> (List<String>) null)
.flatMap(strings -> strings.stream()) // LINE 11
.anyMatch(string -> "gnarly".equals(string)); // LINE 12
Then the exception becomes:
Exception in thread "main" java.lang.NullPointerException
at Test.lambda$main$1(Test.java:11)
at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:271)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at java.base/java.util.stream.Streams$StreamBuilderImpl.tryAdvance(Streams.java:397)
at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:127)
at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:502)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:488)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:230)
at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:196)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:528)
at Test.main(Test.java:12)
Here you see at the top of the stack trace that the real cause is in line 11. A bit confusing indeed, but I guess the JRE cannot report exceptions occurring within method references with line number accuracy.
EDIT: Regarding solutions, my IntelliJ suggests the following:
The filter:
.filter(Objects::nonNull)
.flatMap(Collection::stream)
The null safe lambda:
.flatMap(strings -> strings != null ? strings.stream() : null)