Handling null pointers and throwing exceptions in streams

15,749

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 nulls 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 nulls 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;

REF: javadoc. SRC: openjdk


requireNonNull with a custom message supplier

  list.stream()
      .map(item -> requireNonNull(item, () -> "Expected non-null items. Was: " + list))
      .filter(predicate)

REF: javadoc. SRC: openjdk


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());
Share:
15,749
Manu Joy
Author by

Manu Joy

Updated on June 04, 2022

Comments

  • Manu Joy
    Manu Joy almost 2 years

    Let's consider a Parent class which contains only one Integer 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
    Codebender almost 9 years
    Objects.requireNonNull() also throws NullPointerException only. You need to write a custom action to throw your own exception.
  • user140547
    user140547 almost 9 years
    OP'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
    Codebender almost 9 years
    Also, I guess in both the answers, the MyException should be an unchecked exception. Or the compiler doesn't allow it...
  • Lii
    Lii almost 9 years
    @Codebender: That's right. If it must be checked then a sneaky throw method might be warranted.
  • Holger
    Holger almost 9 years
    The 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
    Lii almost 9 years
    I'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 null Integer.
  • Holger
    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
    Lii almost 9 years
    That's why I threw another exception class in my solution which surrounded the stream operations with a try-catch.
  • Lii
    Lii almost 9 years
    But 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
    Holger almost 9 years
    See 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
    Lii almost 9 years
    Ah, 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
    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 like names.stream().map(Node::new).peek(n -> n.setParent(this)).forEach(this::addNode) instead of longer names.stream().map(name -> {Node n = new Node(name); n.setParent(this); return n;}).forEach(this::addNode).
  • Lii
    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
    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 be names.stream().map(Node::new).forEach(n -> {n.setParent(this); addNode(n);})
  • Lii
    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
    epox over 3 years
    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." docs.oracle.com/javase/10/docs/api/java/util/stream/…
  • Lii
    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
    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/…