处理null对象并在流中抛出异常

时间:2015-07-10 06:54:33

标签: java lambda java-8

让我们考虑一个Parent类,它只包含一个Integer属性。 我创建了6个父类对象,并将一个对象作为null。然后我将这些对象添加到列表中。

我想通过Integer属性的值检索相应的对象。 我使用了Java 8 Streams。

Predicate<Parent> predicate = e -> e.getId() == 100; // sample attribute value
result = list.stream().filter(predicate).collect(Collectors.toList());

但是我得到NullPointerException,所以我编辑了代码:

list.stream().filter(h -> h!=null).filter(predicate).collect(Collectors.toList());

但是如果任何对象为null,我想抛出一个异常。如果列表中没有对象为null,那么我想从列表中检索相应的对象。

如何使用Java 8 Streams使用单个语句实现此目的?

4 个答案:

答案 0 :(得分:14)

JB Nizet的回答没问题,但是它仅使用map作为副作用而不是映射操作,这有点奇怪。当你只对某些事物的副作用感兴趣时,可以使用一种方法,例如抛出异常:peek

List<Parent> filtered = 
    list.stream()
        .peek(Objects::requireNonNull)
        .filter(predicate)
        .collect(Collectors.toList());

如果你想要自己的例外,只需在那里放一个lambda:

List<Parent> filtered = 
    list.stream()
        .peek(p -> { if (p == null) throw new MyException(); })
        .filter(predicate)
        .collect(Collectors.toList());

已检查的例外情况

如果检查了您的例外情况,您可以预先检查空值,如果您不介意两次遍历列表。在您的情况下,这可能是最好的,但可能并非总是可行。

if (list.contains(null)) throw new MyCheckedException();

您还可以在流管道中抛出未经检查的异常,捕获它然后抛出已选中的异常:

try {
    ...
        .peek(p -> { if (p == null) throw new MyException(); })
    ...
} catch (MyException exc) {
    throw new MyCheckedException();
}

偷偷摸摸的投掷

或者你可以采取优雅但有争议的道路并使用偷偷摸摸的方法

但要小心!这种技术绕过了已检查的异常系统,您应该知道自己在做什么。请务必声明周围的方法抛出MyCheckedException!如果你没有,编译器不会警告你,如果检查异常出现在不期望的地方,它可能会导致奇怪的错误。

@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());
}

答案 1 :(得分:3)

让我们从最简单的解决方案开始:

if(list.contains(null)) throw new MyException();
result = list.stream().filter(predicate).collect(Collectors.toList());

如果您怀疑列表包含null,甚至有一个专门的异常类型来标记这种情况,那么预检是最干净的解决方案。这可以确保如果谓词更改为可以处理null的内容或者在使用可能在遇到后续null之前结束的短路流操作时,此类条件不会静默保留。

如果列表中null的出现仍被视为不应发生的编程错误,但您只想更改异常类型(我无法想象这是真正的原因),可能只是抓住并翻译异常:

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

在假设null引用的情况下,这是有效的。如上所述,如果您怀疑列表包含null,您应该更喜欢预先检查。

答案 2 :(得分:1)

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());

答案 3 :(得分:0)

java.utils.Objects#requireNonNull

  list.stream()
      .map(Objects::requireNonNull)
      .filter(predicate)

此代码清楚地将验证部分和过滤器部分分开。在异常调用堆栈中它将包含“ requireNonNull”帧。尽管通过java.util设计,它将增加NPEexceptoin类型。参考:javadoc。 SRC:openjdk


requireNonNull带有静态消息

  list.stream()
      .map(item -> requireNonNull(item, "Null item in the list."))
      .filter(predicate)

导入静态java.util.Objects.requireNonNull;

REF:javadoc。 SRC:openjdk


requireNonNull和自定义消息提供者

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

REF:javadoc。 SRC:openjdk


自定义验证器

  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;

全部验证

如果只需要验证所有项目,请在最后使用收集器。例如。 toArray作为流将实际迭代推迟到“收集”时间:

  list.stream()
      .map(this::validateItemNonNull)
      .toArray();

注意::在生产代码中使用 Stream#map ,并且从不使用 Stream#peek 方法。正如Javadoc所说, peek mainly to support debugging ...where the stream implementation is able to optimize away..., the action will not be invoked for those elements. REF:javadoc