好吧,所有基于 Optional 或 Objects 的解决方案都假定检查器知道其方法的语义,因为它们没有显式注释。
由于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 值抛出异常,因此只有filter 和map 的组合允许删除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 下运行。