Handling null pointers and throwing exceptions in streams
Solution 1
JB Nizet answer is okay, but it uses map
only for its side effects and not for the mapping operation, which is kind of weird. There is a method which can be used when you are solely interested in the side effects of something, such as throwing an exception: peek
.
List<Parent> filtered = list.stream()
.peek(Objects::requireNonNull)
.filter(predicate)
.collect(Collectors.toList());
And if you want your own exception just put a lambda in there:
List<Parent> filtered = list.stream()
.peek(p -> { if (p == null) throw new MyException(); })
.filter(predicate)
.collect(Collectors.toList());
Checked Exceptions
If your exception is checked you can either check for null beforehand, if you don't mind traversing the list twice. This is probably best in your case, but might not always be possible.
if (list.contains(null)) throw new MyCheckedException();
You could also throw an unchecked exception in your stream pipeline, catch it and then throw the checked one:
try {
...
.peek(p -> { if (p == null) throw new MyException(); })
...
} catch (MyException exc) {
throw new MyCheckedException();
}
Sneaky Throw
Or you could take the elegant but controversial road and use a sneaky throw method.
But beware! This technique circumvents the checked exception system and you should know what you are doing. Be sure to declare that the surrounding method throws MyCheckedException
! The compiler won't warn you if you don't, and it will likely cause weird bugs if checked exceptions appear where they are not expected.
@SuppressWarnings("unchecked")
public <T extends Throwable> void throwSneakily(Throwable t) throws T {
throw (T) t;
}
public void m() throws MyCheckedException {
List<Parent> filtered = list.stream()
.peek(p -> { if (p == null) throwSneakily(new MyCheckedException()); })
.filter(predicate)
.collect(Collectors.toList());
}
Solution 2
Let’s start with the simplest solution:
if(list.contains(null)) throw new MyException();
result = list.stream().filter(predicate).collect(Collectors.toList());
If you suspect the list to contain null
s and even have a specialized exception type to flag this condition, a pre-check is the cleanest solution. This ensures that such condition doesn’t silently remain if the predicate changes to something that can handle null
s or when you use a short-circuiting stream operation that may end before encountering a subsequent null
.
If the occurrence of null
in the list still is considered a programming error that shouldn’t happen, but you just want to change the exception type (I can’t imagine a real reason for this), you may just catch and translate the exception:
try {
result = list.stream().filter(predicate).collect(Collectors.toList());
}
catch(NullPointerException ex) {
if(list.contains(null)) // ensure that we don’t hide another programming error
throw new MyException();
else throw ex;
}
This works efficient under the assumption that null
references do not occur. As said, if you suspect the list to contain null
you should prefer a pre-check.
Solution 3
::map vs ::peek
Use Stream#map
and NEVER USE Stream#peek
method in production code. As Javadoc says peek was mainly to support debugging ...where the stream implementation is able to optimize away..., the action will not be invoked for those elements.
REF: javadoc
java.utils.Objects#requireNonNull
list.stream()
.map(Objects::requireNonNull)
.filter(predicate)
This code clearly separates the validation part and filter part. It will contain "requireNonNull" frame in the exception call stack. Although, by java.util design, it'll rise NPE exceptoin type. REF: javadoc. SRC: openjdk
requireNonNull with a static message
list.stream()
.map(item -> requireNonNull(item, "Null item in the list."))
.filter(predicate)
import static java.util.Objects.requireNonNull;
requireNonNull with a custom message supplier
list.stream()
.map(item -> requireNonNull(item, () -> "Expected non-null items. Was: " + list))
.filter(predicate)
custom validator
list.stream()
.map(this::requireItemNonNull)
.filter(predicate)
...
private Parent requireItemNonNull(Parent item) {
if (null == item) throw new IllegalStateException("Expected: non null items. Was: " + this.list);
return item;
validate all
If you only need to validate all items, use a collector at the end. E.g. toArray
as stream postpones actual iteration until the "collect" time:
list.stream()
.map(this::validateItemNonNull)
.toArray();
Solution 4
Function<Parent, Parent> throwingIdentity = p -> {
if (p == null) {
throw new MyException();
}
return p;
};
List<Parent> filtered =
list.stream()
.map(throwingIdentity)
.filter(predicate)
.collect(Collectors.toList());
Manu Joy
Updated on June 04, 2022Comments
-
Manu Joy almost 2 years
Let's consider a
Parent
class which contains only oneInteger
attribute. I created 6 objects of parent class and with one null variable. Then I added these objects to a list.I want to retrieve the corresponding object by the value of
Integer
attribute. I used Java 8 Streams for it.Predicate<Parent> predicate = e -> e.getId() == 100; // sample attribute value result = list.stream().filter(predicate).collect(Collectors.toList());
But I got
NullPointerException
, so I edited the code:list.stream().filter(h -> h!=null).filter(predicate).collect(Collectors.toList());
But I want to throw an exception if any of the object is null. If no objects in the list is null, then I want to retrieve the corresponding object from list.
How can I achieve this using a single statement using Java 8 Streams?
-
Codebender almost 9 yearsObjects.requireNonNull() also throws
NullPointerException
only. You need to write a custom action to throw your own exception. -
user140547 almost 9 yearsOP's question was not to use NPE, so you still need something like Consumer<Parent> con = e -> {if (e.getId() == null) throw new SomeException(); };
-
Codebender almost 9 yearsAlso, I guess in both the answers, the
MyException
should be an unchecked exception. Or the compiler doesn't allow it... -
Lii almost 9 years@Codebender: That's right. If it must be checked then a sneaky throw method might be warranted.
-
Holger almost 9 yearsThe sneaky throw doesn’t work the way you have written. In your code, the compiler will perfectly infer the right checked exception and complain. Besides that,
peek
is meant to support debugging but not to be part of production code logic. -
Lii almost 9 yearsI'll add my comment about catching NPEs here also: I don't really think it's a good practice. I would stick to the rule of always avoiding to catch NPEs. It is too easy to get wrong since they can be thrown basically anywhere. In this example there is a slight chance that there is an unexpected null dereference that is triggered when the list also contains null. Tricky example: If
e.getId()
happens to return a nullInteger
. -
Holger almost 9 years@Lii: that isn’t an issue since, if you have two programming errors, it isn’t really important which one gets flagged. As I already said in the answer, this solution is only for use cases, where the other exception type has similar semantics (programming error) as the NPE and that I therefore don’t see a real value in changing the exception type. And I don’t know whether this is really worse than abusing
peek
or using sneaky throwing technique… -
Lii almost 9 yearsThat's why I threw another exception class in my solution which surrounded the stream operations with a try-catch.
-
Lii almost 9 yearsBut having a null element in the list is likely (or at least not necessarily) not a programming error. Just an exceptional condition which should be reported by an exception.
-
Holger almost 9 yearsSee the first part of my answer. If having a null element in the list is likely not a programming error, a pre-check should be used. I clearly said in the answer that the second part only applies “if the occurrence of
null
in the list still is considered a programming error” (despite the exception type translation). -
Lii almost 9 yearsAh, yes you did, sorry! Then there is only the slight problem of mixing up the programming errors, which, as you noted above, might not be something to worry about.
-
Tagir Valeev almost 9 years@Holger, to my opinion using
peek
in production code might be ok if you setup the processed element for further operation and have no other side-effects likenames.stream().map(Node::new).peek(n -> n.setParent(this)).forEach(this::addNode)
instead of longernames.stream().map(name -> {Node n = new Node(name); n.setParent(this); return n;}).forEach(this::addNode)
. -
Lii almost 9 years@Holger "The sneaky throw doesn’t work": You're right, I made a mistake I've updated. "Besides that,
peek
is meant to support debugging": The documentation says "This method exists mainly to support debugging". If there are no practical problem with it I think it is okay. -
Holger almost 9 years@Tagir Valeev: of course, using
map
for executing a Consumer is not better. But why comparing two wrong approaches anyway? In your example, the correct solution would benames.stream().map(Node::new).forEach(n -> {n.setParent(this); addNode(n);})
-
Lii almost 9 years@Holger: I'm with Tagir about the use of the
peek
method. The reason it is mainly for debugging is not because there is some taboo against using it, but because it is mainly useful for that. But every once in a while it is useful for something else, and then there is no reason not to use it. -
epox over 3 yearsNEVER USE Stream#peek method in production code. As Javadoc says peek was "mainly to support debugging ...where the stream implementation is able to optimize away..., the action will not be invoked for those elements." docs.oracle.com/javase/10/docs/api/java/util/stream/…
-
Lii over 3 years@epox: That's just you inventing an arbitrary rule for yourself. Even if
peek
is mostly useful for debbuging it is fine to use it if you are aware of how it works and that is what you need. There is nothing in the documentation that says otherwise. -
epox almost 3 years@Lii, please read till the end. The key API point is: "...the action will not be invoked for those elements." docs.oracle.com/javase/10/docs/api/java/util/stream/…