Reputation: 1979
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
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