Null annotations,
or even better, null type annotations
can significantly reduce (ideally: eliminate) the danger of NullPointerException
thrown at program runtime.
Unfortunately, the API of many libraries does not yet consistently specify
where null
is allowed and where it isn't.
This can be a major source of incompleteness in this endeavor.
In order to fill this gap, starting with Eclipse 4.5 (Mars),
JDT supports the concept of external annotations,
which means that null annotations can be specified in separate files – without modifying the original library.
If you attach such external null annotations to a given library,
JDT will then consider these annotations for its static null analysis.
This help page describes
External annotations can be provided as a directory tree,
whose leaves are text files with extension .eea
("Eclipse External Annotations").
In this structure, each directory corresponds to a Java package,
and each .eea
file corresponds to a Java type (class, interface etc.).
Optionally, the above structure can be packed in a single zip file with extension .zip
or .jar
.
The exact format of .eea
text files has been designed with slight bias towards processing by tools.
Still, these files are amenable to storing, comparing and merging using any version control system.
The format is based on signatures as defined in
JVMS 4.7.9.1.
Caveat: When different projects referring to the same library have different configurations with respect to external annotations, the UI support described below (command Annotate and Javadoc hovers) can easily get confused: When looking at a given library class, JDT may not know which project the user is currently working on, producing unexpected results. While hopefully this will be improved in a future version, for the time being users are advised to use the same external annotation location for all projects referring to the same library. A shared ("common") base project will typically be a good choice for hosting external annotations.Two different strategies exist for declaring locations where external annotations should be search:
External annotations can be directly attached to each JRE configured in the current workspace. For this purpose, please use the Installed JREs preference page and click Edit for details of the selected JRE. After selecting one or more Jar files contributed by the given JRE installation, click External Annotations to open a new dialog, where either a directory or a zip file can be selected as the external annotation location for the selected jar(s).
Alternatively, you may select the properties of a specific JRE bound in the Java Build Path of a specific project. Please note, that attaching external annotations to individual jars of a JRE always affects the workspace preference, even if accessed via the project's build path. The only way to define a project-specific location for external annotation location for a JRE is via the direct child External annotations: of the top-level node called JRE System Library, but note, that project specific locations are not recommended, see the "Caveat" above.
Depending on the build technology in use, projects may have additional classpath containers like Plug-in Dependencies or Maven Dependencies. For these containers, the only option currently is to specify the external annotation location via each projects build path. In this case users are advised to manually ensure that the same location is used for all projects of the workspace, see the "Caveat" above.
For any other libraries (jars) explicitly referenced from the Java Build Path of a project, external annotations can be attached separately in the Libraries page of the project's build path.
Pre-requisites: External annotations require a library with source attachment. Additionally, annotation based null analysis must be enabled for the current project, an external annotation location must be defined for the library and that location must be a directory (not a zip file) within the workspace.
After navigating to the desired class, you may select the type (method parameter or method return type) that should be affected by an annotation, and invoke the new command Annotate (available via context menu and by default bound to Ctrl-1). Similar to quick assists in a Java source code editor, this command will offer proposals applicable at the current location. For external annotations, the three options are:
For obvious reasons, exactly 2 of these three proposals are offered at any suitable location.
Behind the scenes this command will create and/or update an .eea
file corresponding to the current class.
The new annotation will then be respected by the compiler:
Errors and warnings in an open editor will be updated immediately;
The Problems view will be updated when affected classes are re-compiled.
Be careful: When attaching external annotations to a library, keep in mind that you are defining a new contract, that will then be used to check your program against. The compiler will, however, not check the library, whether it actually conforms to this contract. By carelessly marking, e.g., a return type as@NonNull
you let the compiler advise you to remove null checks against return values from the given method. If the library does return null in any situation, this null will hit you unguarded.
Before adding an annotation, make sure it is backed by facts, like, e.g.:
- explicit mention in Javadoc, e.g., when a
@return
specification says: "... or null when ...".- an explicit null check of a method parameter, which does not throw an exception (implying that null is accepted →
@Nullable
parameter)- uncheck dereference of a method parameter (implying that null is not accepted →
@NonNull
parameter)- an explicit
null
return (implying a@Nullable
return type)- explicitly created return values at each return statement (implying a
@NonNull
return type).
The immediate effect of external annotations can be observed via changes in compiler errors / warnings.
For full transparency, annotated library signatures can be inspected using the Javadoc hover
or Javadoc view.
Due to an implementation limitation, this feature currently only works for
type annotations (Java 8).
Generally, support for external annotations has been developed with regard to fully supporting all locations of null type annotations (Java 8). This means, you may refer to any detail of a type in a method signature and annotate, e.g., a type parameter, a type bound or an array dimension. When using declaration annotations, the relevant subset of location shares the same mechanisms for external annotations.
In one situation the design for type annotations shines through even when using declaration annotations:
annotating an array type is based on the new syntax of type annotations (see also Compatibility > Syntax).
For illustration consider the following method in type Collection
:
Object [] toArray();
Even when a project uses Java 7 or below, the return type of this method is annotated by placing the cursor before the array brackets. Invoking Annotate will create an external annotation that is internally interpreted as an annotation on the method (but still the meaning is: describing the return of this method).
Other than this one exception, external annotations closely mimic how annotations directly within a source file would be handled, with a reduced set of locations for declaration annotations, and the full set of locations for type annotations.