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

Man*_*Joy 8 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());
Run Code Online (Sandbox Code Playgroud)

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

list.stream().filter(h -> h!=null).filter(predicate).collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

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

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

Lii*_*Lii 14

JB Nizet的答案还可以,但map它只用于副作用而不用于映射操作,这有点奇怪.有一种方法可以在你对某些事物的副作用感兴趣时使用,例如抛出异常:peek.

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

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

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

已检查的例外情况

如果您的异常被选中,您可以预先检查null,如果您不介意两次遍历列表.在您的情况下,这可能是最好的,但可能并非总是可行.

if (list.contains(null)) throw new MyCheckedException();
Run Code Online (Sandbox Code Playgroud)

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

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

鬼鬼祟祟的投掷

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

但要小心!这种技术绕过了检查过的异常系统,你应该知道你在做什么.一定要声明周围的方法抛出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());
}
Run Code Online (Sandbox Code Playgroud)

  • @Holger,我认为在生产代码中使用`peek`可能没问题,如果你为进一步操作设置处理过的元素并且没有其他副作用,比如`names.stream().map(Node :: new).peek( n - > n.setParent(this)).forEach(this :: addNode)`而不是更长`names.stream().map(name - > {Node n = new Node(name); n.setParent(this) ; return n;}).forEach(this :: addNode)`. (3认同)
  • 偷偷摸摸的投掷不像你写的那样工作。在你的代码中,编译器会完美地推断出正确的检查异常并抱怨。除此之外,`peek` 旨在支持调试,但不是生产代码逻辑的一部分。 (2认同)
  • @Holger"_偷偷摸摸的投掷不起作用_":你是对的,我犯了一个错误,我已经更新了."_除此之外,`peek`意味着支持debugging_":文档说"_这个方法存在**主要**以支持debugging_".如果没有实际问题,我认为没关系. (2认同)