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.
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.
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:
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.
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).
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.
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.
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 |
|
|
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.
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.