looper
looper

Reputation: 1979

Filter nullable values from stream and updating nullable property

If I have a Stream<@Nullable Object>, I can filter null-elements like that:

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

This, however, returns a Stream of nullable Objects. Is there an elegant way to return a Stream of NonNull elements? At this place I already know that the elements can't be null.

This is what I've come up with, but it's too long, imo:

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

(This implies that Optional::get will always return NonNull-values)

Upvotes: 3

Views: 530

Answers (1)

Holger
Holger

Reputation: 298143

Well, all solutions based on Optional or Objects assume that the checker knows the semantics of their methods, as they don’t have explicit annotations.

Since filter never changes the type, it would require deep support from the checker to model this as a Stream<@Nullable X>Stream<@NonNull X> transition, even if it understands the semantics of the filter function.

The simpler approach is to use map as it allows changing the element type:

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

Here, the filter operation ensures that there are no null elements, but doesn’t change the formal element type. In contrast, the map operation allows changing the formal type, and the function Objects::requireNonNull converts a nullable input into a nonnull output, assuming that the audit tool knows about that, as said, these JRE provided methods have no annotations.

Since requireNonNull will throw an exception for null values, only the combination of filter and map allows the desired behavior of removing null values and changing the formal element type.

If the audit tool does not understand the semantic of the JRE method, you’ll have to create an equivalent method yourself, then with the annotations,

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

which should be recognized as correct by any reasonable checker, and use it as

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

Your Optional based approach can be simplified with Java 9:

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

which can be simplified even further:

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

but of course, this again requires a tool understanding these methods. Or you re-implement the logic:

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

which then works under Java 8 as well.

Upvotes: 2

Related Questions