Java 8流 - 如何正确地创建NPE安全流

4
上周在处理流时遇到了一个非常奇怪的NPE,这给我带来了很多麻烦,所以现在在使用Stream时,我感觉需要更加小心谨慎地处理NPE。
以下是我的当前方法:
private boolean matchSomeError(final List<ErrorAtMessageLevel> errorList) {
    return errorList.stream()
        .filter(errorAtMessageLevel -> errorAtMessageLevel.getErrorSegment() != null && errorAtMessageLevel.getErrorSegment().getErrorDetails() != null)
        .map(errorAtMessageLevel -> errorAtMessageLevel.getErrorSegment().getErrorDetails())
        .anyMatch(errorDetails -> SOME_FANCY_ERROR_CODE.equals(errorDetails.getErrorCode()));
}

我的问题是,我处理的是外部POJO,因此无法更改它并使其具有空值安全性,因此我必须调整我的代码。
以下是一些限制: 1)errorList-这里不能为null,因此调用.stream()是安全的-当它为空时,它只会返回false 2)getErrorSegment()getErrorDetails()都可以为null,这就是为什么我使用filter的原因,以确保它们都不为null 3)getErrorCode()可能为null,但它永远不会抛出NPE,因为与null匹配时它只会返回false-对我来说没关系。
你会如何改进这个流?我觉得我的.filter()很糟糕,它可以变得更好。最近写了很多这样的代码,因为我不确定流如何处理null,并且不想在.map()中调用null时得到NPE。

那个 NPE 弹出在哪里了? - Antoniossss
1
如果您无法更改列表中的内容,那么这就是您能做的最好的事情。流不会改变Java的工作方式:如果您在null上调用方法,则无论您是否使用流,都将获得NPE。 - JB Nizet
也许你可以建议更改外部API,使其返回一个Optional - MC Emperor
如果我不使用.filter()进行两层深度检查,那么在.map()中将会弹出null。 @JBNizet希望你们有一些聪明的解决方案来正确过滤它 ;) - doublemc
你再次确实已经正确地对其进行了过滤。 - JB Nizet
如果您对所调用的代码有任何控制权,请避免使用“null”,而不是避免“NPE”。 - Boris van Katwijk
2个回答

8
您可以更优雅地过滤掉null,方法如下:
private boolean matchSomeError(final List<ErrorAtMessageLevel> errorList) {
    return errorList.stream()
        .map(ErrorAtMessageLevel::getErrorSegment)
        .filter(Objects:nonNull)
        .map(ErrorSegment::getErrorDetails)
        .filter(Objects:nonNull)
        .anyMatch(errorDetails -> SOME_FANCY_ERROR_CODE.equals(errorDetails.getErrorCode()));
}

2

我认为最好的方法是定义一个单独的方法,使用Optional来避免空值。然后在您的流中使用方法引用来引用它:

return errorList.stream()
    .map(MyClass::getErrorCode)
    .flatMap(Optional::stream) // from Java 9, else filter then map to Optional.get
    .anyMatch(SOME_FANCY_ERROR_CODE::equals);


private static Optional<String> getErrorCode(final ErrorAtMessageLevel error)
{
    return Optional.ofNullable(error)
        .map(ErrorAtMessageLevel::getErrorSegment)
        .map(ErrorSegment::getErrorDetails)
        .map(ErrorDetails::getErrorCode);
}

您的方法引用可能使用不同的类名。由于代码没有显示使用哪些类,因此我只能做出合理的猜测。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接