处理空指针并在流中引发异常

2022-09-03 18:35:47

让我们考虑一个只包含一个属性的类。我创建了6个父类对象,并带有一个空变量。然后,我将这些对象添加到列表中。ParentInteger

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

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

但是我想在任何对象为空时引发异常。如果列表中没有对象为空,则我想从列表中检索相应的对象。

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


答案 1

JB Nizet的答案是可以的,但它只用于副作用,而不是映射操作,这有点奇怪。当您只对某些事情的副作用感兴趣时,可以使用一种方法,例如抛出一个异常:。mappeek

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

已检查的异常

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

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

答案 2

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

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

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

如果列表中出现的 仍然被认为是不应该发生的编程错误,但你只是想改变异常类型(我无法想象这是真正的原因),你可以只是捕获并转换异常: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;
}

这在假设不会发生引用的情况下是有效的。如前所述,如果您怀疑该列表包含,则应首选预检查。nullnull


推荐