Annotation Interface NonNull
null
as a value; rarely needed within null-marked code.
This annotation serves two primary purposes:
- To mark any sporadic non-null type usages inside a scope that is not ready to be fully
NullMarkedyet. - To perform a non-null projection of a type variable, explained below.
For a comprehensive introduction to JSpecify, please see jspecify.org.
Non-null projection
In the following example, MyOptional's type parameter T accepts only non-null
type arguments, but MyList's type parameter E will accept either a non-null or
nullable type argument.
// All the below is null-marked code
class MyOptional<T> { … }
interface MyList<E extends @Nullable Object> {
// Returns the first non-null element, if such element exists.
MyOptional<E> firstNonNull() { … } // problem here!
}
MyList<@Nullable String> maybeNulls = …
MyList<String> nonNulls = …
Because MyOptional accepts only non-null type arguments, we need both
maybeNulls.firstNonNull() and nonNulls.firstNonNull() to produce the same return type:
MyOptional!<String!> (see notation).
However, as specified above, they won't do that. In fact, there is a problem with the
firstNonNull signature, since the type argument String? would not meet the requirements
of MyOptional's type parameter.
The solution is to project the type argument to its non-null counterpart:
// Returns the first non-null element, if such element exists.
MyOptional<@NonNull E> firstNonNull() { … } // problem fixed!
Here, @NonNull E selects the non-null form of the type argument, whether it was
already non-null or not, which is just what we need in this scenario.
If E has a non-null upper bound, then the apparent projection @NonNull E is
redundant but harmless.
Nullable projection serves the equivalent purpose in the opposite direction, and is far more commonly useful.
If a type variable has all its usages being projected in one direction or the other, it should be given a non-null upper bound, and any non-null projections can then be removed.
Where it is applicable
@NonNull is applicable in all the same
locations as Nullable.