从流中过滤可空值并更新可空属性

时间:2016-07-19 11:36:25

标签: java java-stream

如果我有Stream<@Nullable Object>,我可以过滤掉这样的空元素:

Stream<@Nullable Object> s = str.filter(Objects::nonNull);

然而,这会返回一个可以为空的对象流。是否有一种优雅的方式来返回NonNull流元素?在这个地方,我已经知道元素不能为空。

这就是我想出的,但它太长了,imo:

Stream<@NonNull Object> s = str.map(Optional::ofNullable).filter(Optional::isPresent).map(Optional::get)

(这意味着Optional :: get将始终返回NonNull值)

1 个答案:

答案 0 :(得分:2)

好吧,基于OptionalObjects的所有解决方案都假设检查器知道其方法的语义,因为它们没有明确的注释。

由于filter永远不会更改类型,因此需要检查程序的深度支持才能将其建模为Stream<@Nullable X>Stream<@NonNull X>转换,即使它理解了过滤器函数的语义

更简单的方法是使用map,因为它允许更改元素类型:

Stream<@NonNull Object> s = str.filter(Objects::nonNull).map(Objects::requireNonNull);

这里,过滤操作确保没有null个元素,但不会更改形式元素类型。相反,map操作允许更改正式类型,函数Objects::requireNonNull将可空输入转换为非空输出,假设审计工具知道这一点,如上所述,这些JRE提供的方法具有没有注释。

由于requireNonNull会为null值引发异常,因此只有filtermap的组合才能实现删除null值和更改所需的行为形式元素类型。

如果审计工具不理解JRE方法的语义,您必须自己创建一个等效方法,然后使用注释,

class MyObjUtil {
    public static <T> @NonNull T requireNonNull(@Nullable T obj) {
        if(obj==null) throw new NullPointerException();
        return obj;
    }
}

应由任何合理的检查员识别为正确,并将其用作

Stream<@NonNull Object> s = str.filter(Objects::nonNull).map(MyObjUtil::requireNonNull);

使用Java 9可以简化基于Optional的方法:

Stream<@NonNull Object> s = str.flatMap(o -> Optional.ofNullable(o).stream());

可以进一步简化:

Stream<@NonNull Object> s = str.flatMap(o -> Stream.ofNullable(o));

但当然,这又需要一个工具来理解这些方法。或者你重新实现逻辑:

class MyStreamUtil {
    public static <T> Stream<@NonNull T> ofNullable(@Nullable T obj) {
        return obj==null? Stream.empty(): Stream.of(obj);
    }
}
Stream<@NonNull Object> s = str.flatMap(o -> MyStreamUtil.ofNullable(o));

然后在Java 8下工作。