Java 8's new Type Annotations
Java 8 introduces two important changes to Annotations designed to help developers produce better code and improve the accuracy of automated code analysis to verify that quality.
Quick Annotations Webinar
There is a great video explaining the new improvements in the Java 8 Launch Webinars called “Enhanced Metadata - Annotations and Access to Parameter Names” by Alex Buckley and Michael Ernst.
Annotation Improvements
Type Annotations allow developers to write annotations in more places than before. The compiler can then verify these annotations, for example identifying uses of null values, accidental value modifications, and cases where data crosses a trust boundary without proper validation. By moving some annotatable information from the Javadoc (understood only by people) and into the code (understood by both people and analyzers), it is easier to understand intent and verify the absence of certain errors.
Repeating Annotations make it easier for authors of these annotations because there is less need for wrapper annotations.
The Checker Framework provides a few Type Annotations that could benefit both library and application developers, such as:
- @NonNull – The compiler can determine cases where a code path might receive a null value, without ever having to debug a NullPointerException.
- @ReadOnly – The compiler will flag any attempt to change the object. This is similar to Collections.unmodifiableList, but more general and verified at compile time.
- @Regex – Provides compile-time verification that a String intended to be used as a regular expression is a properly formatted regular expression.
- @Tainted and @Untainted – Identity types of data that should not be used together, such as remote user input being used in system commands, or sensitive information in log streams.
- @m – Units of measure ensures that numbers used for measuring objects are used and compared correctly, or have undergone the proper unit conversion.
Putting Type Annotations on your code
Java SE 8 allows type annotations anywhere that a type is used. Previously, annotations were only allowed on definitions. Some examples of this are:
Annotation Example | Meaning |
---|---|
@NonNull List<String> | A non-null list of Strings. |
List<@NonNull String> | A list of non-null Strings. |
@Regex String validation = "(Java|JDK) [7,8]" | Check at compile time that this String is a valid regular expression. |
private String getInput(String parameterName){ final String retval = @Tainted request.getParameter(parameterName); return retval; } | The object assigned to retval is tainted and not for use in sensitive operations. |
private void runCommand(@Untainted String… commands){ ProcessBuilder processBuilder = new ProcessBuilder(command); Process process = processBuilder.start(); } | Each command must be untainted. For example, the previously tainted String must be validated before being passed in here. |
For reading annotations, the way to look at them is that they annotate the next item after that isn’t also an annotation.
Automating issue detection
When working on software, it helps to uncover potential problems early. A problem caught early is easier to fix than one caught later, and a potential problem caught right away is easier still. Some annotations allow problems to be caught immediately. The @Override
annotation allows the compiler (or a static analysis tool) to immediately determine if a developer wrote the wrong method signature.
Other annotations, like @NonNull
and @Readonly
can be used by analyzers like the Checker Framework, FindBugs, Eclipse, NetBeans, IntelliJ, or a commercial analyzer. Those analyzers can then be run at compile time, through IDE background compilation, Ant/Maven, or continuous integration.
Type Annotations tell those analyzers what to look for. Without the Type Annotations in place, these analyzers would still be able to locate null-usage and write-modifications but would not know that they are wrong. The result would then be false negatives (no issue reported) or false positives (incorrect issues reported).
Teamwork
Type Annotations can greatly benefit teams that are geographically distributed or contain many members. By placing Type Annotations inside the code and running automated checks before commits or during integration builds, team members can identify situations where one change inadvertently affects another.
Optional Type Annotations are not a substitute for runtime validation
Before Type Annotations, the primary location for describing things like nullability or ranges was in the javadoc. With Type annotations, this communication comes into the bytecode in a way for compile-time verification.
Your code should still perform runtime validation.
Annotation validation versus Business Validation
Type Annotations are best used with common forms of validation that relate to computer science. There are certain types of business validation that are not applicable.
Well-suited for Type Annotations
Likely not well-suited for Type Annotations
Null value checks.
Numeric range checks.
Basic type checks, such as regular expressions.
Assignments and updates (e.g. read-only)
Dataflow validation detection (e.g. have the incoming function arguments gone through the right validation functions)
This function cannot be executed outside certain hours or on government holidays.
Access to a feature requires a certain account-type.
Appendix
Annotations in core Java
There is no set of default type annotations available out of the box in the Java SE 8 platform. All previous examples in this post used the Checker Framework. The Type Annotations in Java SE 8 focused on the ability to put annotations in the right areas to describe a program. A separate, currently inactive JSR-305 (not part of Java 8) exists for identifying what those annotations should be.
The Checker Framework currently uses Java Annotation Index Files to gain comparable support for the core Java runtime and targeted libraries, or previous Java versions like Java SE 7 or 6.
Removal of APT
JDK 8 also removes a legacy annotation processing tool, named apt. Few users should be affected by this change. This was done as part of JEP 117 because everything required for annotation processing appears in either javax.annotation.processing or javax.lang.model.
https://blogs.oracle.com/java/post/java-8s-new-type-annotations