Annotation Interface NullMarked
@Documented
@Target({MODULE,PACKAGE,TYPE,METHOD,CONSTRUCTOR})
@Retention(RUNTIME)
public @interface NullMarked
Indicates that the annotated element and the code transitively enclosed within it are null-marked
code: there, type usages are generally considered to exclude
null
as a value unless
specified otherwise. Using this annotation avoids the need to write @NonNull
many
times throughout your code.
For a comprehensive introduction to JSpecify, please see jspecify.org.
Effects of being null-marked
Within null-marked code, as a general rule, a type usage is considered non-null (to
exclude null
as a value) unless explicitly annotated as Nullable
. However, there
are several special cases to address.
Special cases
Within null-marked code:
- We might expect the type represented by a wildcard (like the
?
inList<?>
) to be non-null, but it isn't necessarily. It's non-null only if itextends
a non-null type (like inList<? extends String>
), or if the class in use accepts only non-null type arguments (such as ifList
were declared asclass List<E extends String>
). But ifList
does accept nullable type arguments, then the wildcards seen inList<?>
andList<? super String>
must includenull
, because they have no "upper bound". (Why?)- Conversely, a type parameter is always considered to have an upper bound; when
none is given explicitly,
Object
is filled in by the compiler. The exampleclass MyList<E>
is interpreted identically toclass MyList<E extends Object>
: in both cases the type argument inMyList<@Nullable Foo>
is out-of-bounds, so the list elements are always non-null. (Why?)
- Conversely, a type parameter is always considered to have an upper bound; when
none is given explicitly,
- Otherwise, being null-marked has no consequence for any type usage where
@Nullable
and@NonNull
are not applicable, such as the root type of a local variable declaration. - When a type variable has a nullable upper bound, such as the
E
inclass Foo<E extends @Nullable Bar>
), an unannotated usage of this type variable is not considered nullable, non-null, or even of "unspecified" nullness. Rather it has parametric nullness. In order to support both nullable and non-null type arguments safely, theE
type itself must be handled strictly: as if nullable when "read from", but as if non-null when "written to". (Contrast withclass Foo<E extends Bar>
, where usages ofE
are simply non-null, just like usages ofString
are.) - By using
NullUnmarked
, an element within null-marked code can be excluded and made null-unmarked, exactly as if there were no enclosing@NullMarked
element at all.
Where it can be used
@NullMarked
and @NullUnmarked
can be used on any package, class,
method, or constructor declaration; @NullMarked
can be used on a module declaration as
well. Special considerations:
- To apply this annotation to an entire (single) package, create a
package-info.java
file there. This is recommended so that newly-created classes will be null-marked by default. This annotation has no effect on "subpackages". Warning: if the package does not belong to a module, be very careful: it can easily happen that different versions of the package-info file are seen and used in different circumstances, causing the same classes to be interpreted inconsistently. For example, a package-info file from atest
source path might hide the corresponding one from themain
source path, or generated code might be compiled without seeing a package-info file at all. - Although Java permits it to be applied to a record component declaration (as in
record Foo(@NullMarked String bar) {...}
), this annotation has no meaning when used in that way. - Applying this annotation to an instance method of a generic class is acceptable, but is not recommended because it can lead to some confusing situations.
- An advantage of Java modules is that you can make a lot of code null-marked with
just a single annotation (before the
module
keyword).NullUnmarked
is not supported on modules, since it's already the default. - If both
@NullMarked
and@NullUnmarked
appear together on the same element, neither one is recognized.