Annotation 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 ? in List<?>) to be non-null, but it isn't necessarily. It's non-null only if it extends a non-null type (like in List<? extends String>), or if the class in use accepts only non-null type arguments (such as if List were declared as class List<E extends String>). But if List does accept nullable type arguments, then the wildcards seen in List<?> and List<? super String> must include null, 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 example class MyList<E> is interpreted identically to class MyList<E extends Object>: in both cases the type argument in MyList<@Nullable Foo> is out-of-bounds, so the list elements are always non-null. (Why?)
  • 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 in class 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, the E type itself must be handled strictly: as if nullable when "read from", but as if non-null when "written to". (Contrast with class Foo<E extends Bar>, where usages of E are simply non-null, just like usages of String 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 a test source path might hide the corresponding one from the main 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.