Package org.jspecify.annotations
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.
Warning: These annotations are under development, and any aspect of their naming, locations, or design is subject to change until the JSpecify 1.0 release. Moreover, supporting analysis tools will be tracking the changes on varying schedules. Releasing a library using these annotations in its API is strongly discouraged at this time.
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.