#Retention of Java type checker annotations - java

The Java 8 type annotations (JSR 308) allow type checkers to perform static code analysis. For example, The Checker Framework can check for possible nullness via #NonNull annotations.
Various projects define their own NonNull annotations, for example:
org.checkerframework.checker.nullness.qual.NonNull
edu.umd.cs.findbugs.annotations.NonNull
javax.annotation.Nonnull
javax.validation.constraints.NotNull
lombok.NonNull
org.eclipse.jdt.annotation.NonNull
etc. (see The Checker Framework Manual, section 3.7)
For such annotations, I would expect the #interface to have #Retention(RetentionPolicy.CLASS), because they are usually not needed at runtime. Most importantly, the code does not have any runtime dependencies on the respective library.
While org.eclipse.jdt.annotation.NonNull follows this approach, most other NonNull annotations, like javax.annotation.Nonnull (JSR 305) and org.checkerframework.checker.nullness.qual.NonNull itself, have #Retention(RetentionPolicy.RUNTIME). Is there any particular reason for the RetentionPolicy.RUNTIME in these annotations?
Clarification: The Checker Framework supports annotations in comments for backward compatibility. However, using those in Java 8 just to avoid runtime dependencies seems like a dirty hack.

This is a good question.
For the purpose of static checking at compile time, CLASS retention would be sufficient. Note that SOURCE retention would not be sufficient, because of separate compilation: when type-checking a class, the compiler needs to read the annotations on libraries that it uses, and separately-compiled libraries are available to the compiler only as class files.
The annotation designers used RUNTIME retention to permit tools to perform run-time operations. This could include checking the annotations (like an assert statement), type-checking of dynamically-loaded code, checking of casts and instanceof operations, resolving reflection more precisely, and more. Not many such tools exist today, but the annotation designers wanted to accommodate them in the future.
You remarked that with #Retention(RetentionPolicy.CLASS), "the code does not have any runtime dependencies on the respective library." This is actually true with #Retention(RetentionPolicy.RUNTIME), too! See this Stack Overflow question:
Why doesn't a missing annotation cause a ClassNotFoundException at runtime? .
In summary, using CLASS retention costs a negligible amount of space at run time, enables more potential uses in the future, and does not introduce a run-time dependency.
In the case of the Checker Framework, it offers run-time tests such as isRegex(String). If your code uses such methods, your code will be dependent on the Checker Framework runtime library (which is smaller than the entire Checker Framework itself and has a more permissive license).

Each annotation has it's purpose!
javax.validation.constraints.NotNull
This ones is defined by the bean validation specification and is used to perform non-null check at runtime, so it needs to be retained at runtime to perform, for example, a form valdiation ...
#RetentionPolicy.SOURCE => usually used for documentation
#RetentionPocily.CLASS => allow to give some information to the compiler but not the JVM (for example, to perform code generation during compilation)
#RetentionPolicy.RUNTIME => allow to retrieve annotation information at the JVM level (so at runtime).
Regards,
Loïc

Related

Compatibility of a Java runtime retention annotation in previous Java versions

I want to use the #FunctionalInterface from Java 8 in my code, but I want to be able to use the generated class files with Java 6. I think then that I should the source version to 1.8, and the target version to 1.6.
I would be using #FunctionalInterface just for documentation, but I note that it has #Retention(RetentionPolicy.RUNTIME). If no one ever uses that annotation, will it cause problems?
If someone iterates over the annotations of my object at runtime, will it cause a missing class exception? But if that is true, how is it that how Google Guava can declare the JSR 305 annotation dependency to have a Maven <scope> of provided, which means annotations such as javax.annotation.Nonnull are missing at runtime, too, in Guava, without causing problems?
Let me ask it another way: if I use Google Guava in my project but don't include a JSR 305 dependency, do I really risk some error if I use reflection on the code? If so, what error will occur? If no error will occur, then analogously can I use the #FunctionalInterface annotation in source compiled with Java version 1.8 yet targeted to version 1.6 without any risk of runtime errors, even using reflection?
I think then that I should [set] the source version to 1.8, and the target version to 1.6.
Actually, it is not possible to compile Java source files of newer source versions for older JVM target versions. Oracles and OpenJDKs javac will reject a compilation attempt where the -source version is higher than the -target version. (However, I couldn't find a specification denying it, even the manual doesn't mention that). The sole idea of javacs cross-compiling feature is that you can compile your old e.g. 1.6 Java files still for the old 1.6 JVM even when you are using a newer JDK for compilation.
The issue you are describing is the sort of reason for this. Since Java is using a sort of lazy dependency loading, the compiler can't guarantee that there will be an appropriated class at runtime for all the dependencies. This also applies to the standard library.
However, there are (unofficial) tools to compile the newer source idioms or byte code to older byte code versions. But that doesn't go for the standard library. If you wanna use newer classes, you have to provide them on your own. For this purpose, there exist some back ports for specific parts of the standard library.
Specifically about your annotation question:
I was not able to find any reliable specification to what should/might happen if the JVM encounters an annotated construct for which it could not retrieve the class file (I searched the Java virtual machine specification SE 8). However, I found a somewhat related reference in the Java language specification SE 8:
An annotation is a marker which associates information with a program construct, but has no effect at run time.
From JLS 9.7
This statement rather indicates that an annotation (present or not) should not have an influence on the execution of a JVM. Therefore, a exception (such as NoClassDefFoundError) because of a missing annotation were rather against this.
Finally, though the answers of this question, I found even more specific statements:
An annotation that is present in the binary form may or may not be available at run time via the reflection libraries of the Java SE platform.
From JLS 9.6.4.2
And
Adding or removing annotations has no effect on the correct linkage of the binary representations of programs in the Java programming language.
From JLS 13.5.7
This quite clearly states that missing annotations will not cause an error, but instead will be just ignored if examined by reflection.
And if you deliver a class annotated with a Java 1.8 standard library annotation, and it will be (somehow) executed on e.g. Java 1.6 JVM where that annotation is just not present, then this specifications denies that any error is generated.
This is also supported by the following test which I wrote: (notice the usage of reflection)
#TestAnno
public class Test {
public static void main(String[] args) {
Annotation[] annos = Test.class.getAnnotations();
for (Annotation a : annos) {
System.out.println(a);
}
}
}
#Retention(RetentionPolicy.RUNTIME)
#interface TestAnno {
}
If compiled, it yields a Test.class and a TestAnno.class. When executed the program outputs:
#TestAnno()
Because that is the one annotation applied to Test. Now, if the TestAnno.class is removed without any modifications to Test.class (which refers to TestAnno with LTestAnno; sequence in the byte code) and Test is executed again, it just does not output anything. So my JVM is indeed ignoring the missing annotation and does not generate any error or exception (Tested with a OpenJDK version 1.8.0_131 on Linux).
As with any class loading situation, if the class isn't needed (or rather, doesn't need to be loaded), it doesn't matter if the class doesn't exist at runtime. Runtime annotations normally have the same problem, since if they're retained at runtime, it usually means that there's logic based on them, meaning their classes are loaded too.
But #FunctionalInterface doesn't have runtime logic, so...
Why does #FunctionalInterface have a RUNTIME retention? Apparently not for any particularly compelling reason, just a side effect of it also being #Documented annotation.
So if you want to make sure there are no potential problems if someone (or some tool more likely (I don't mean a "tool", like a co-worker)) decides to enumerate the annotations in your classes, I guess you'd need to remove the annotations at pre-processing.

Annotation retention policy: what real benefit is there in declaring `SOURCE` or `CLASS`?

I know there are three retention policies for Java annotations:
CLASS: Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time.
RUNTIME: Annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.
SOURCE: Annotations are to be discarded by the compiler.
And although I understand their usage scenarios, I don't get why it is such an important thing to specify the retention policy that retention policies exist at all.
I mean, why aren't all the annotations just kept at runtime? Do they generate so much bytecode / occupy so much memory that stripping those not declared as RUNTIME does make that much difference?
To actually answer the question: to reduce dependencies. If there would be no distinction between e.g. SOURCE and RUNTIME, the "user" of the classes would have to provide all the dependencies the annotations come from. So only because that annotation is used by the IDE (SOURCE), the jar would have to be provided during runtime - which is unnecessary. If you try to get annotations from a class, where no jar is provided that actually provides the annotation, you will get a class not found exception.
Retention policy SOURCE is to aid IDEs, compilers and possibly code/doc generators to take advantage of annotations. These annotation do not make up part of compiled class and are discarded by compiler so not available during runtime.
For example, annotation java.lang.SuppressWarnings tells compiler not to report certain warnings.
Annotations to generate documentation can be of retention policy SOURCE.
Take a look at this post/answer Annotation SOURCE Retention Policy

Java Annotation Processing - Purpose & Examples

Even after spending a good time, I am unable to understand the purpose of Annotation Processing.
I understand why annotations are required for run-time, simplest example I can think are:
Replacement of marker interface.
Replacement of market properties of a type (e.g. transient)
In general, any usefulness that can be done at runtime.
But unfortunately, i could not understand any practical example/reason of using annotation at compile time(except for default annotations provided by JDK e.g. #Override, etc).
I could not understand what is the purpose/need of 'generating code' using Annotation Processors.
Edit: Javadoc/Custom Java doc is one utility I can think of as a purpose of using annotation processors.
This can be used for all sorts of things.
Two simple examples
The Lombock project. Tired of writing thousands of getters and setters? Why not let an annotation processor do it at compile time.
AOP. You can use something like AspectJ to weave in code dependent on annotations. This would be done post compile but as part of the compilation process. For example Spring AOP uses the #Transactional annotation in combination with AspectJ to weave transaction code around methods marked with the annotation.
There are many other uses, but they generally break down into two categories
To reduce boiler plate code.
For cross-cutting concerns.
There are two main purposes of the annotation processing environment - analysis and code generation.
The analysis permits you to extend the capabilities of the java compiler, analyzing program elements as they are being compiled, possibly adding additional constraints, validations, and reporting errors and warnings for violations of those constraints.
Code generation permits you to generate additional supplementary code from signals in your existing hand-written code, primarily (though not exclusively) keyed off of Annotations.
Some examples include Dagger, which is a system for compile-time-analyzed dependency injection, reporting errors and warnings normally found at runtime instead during the compilation of the code. Dagger also generates all the code that would normally be done with reflection, or by hand-writing glue-code, providing substantial performance benefits (in some cases) as well as infrastructure code which is available for step-through debugging, etc.
Another example is the Checker Framework which evaluates a variety of checks against your code, including null safety, etc.
A third example is Auto-Value intended to make small value types nearly trivial to write.
One thing the annotation processing environment is decidedly not suited for is mutation of existing code in place, or modification of code currently under compilation. While some projects do this, they are not actually using the annotation processor APIs but casting to internal compiler types to do so. While this is clearly possible, it's potentially brittle, and may not work reliably from version to version, or compiler to compiler, requiring custom handling for each version and compiler vendor.

Annotation SOURCE Retention Policy

From the Java doc:
CLASS:
Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time.
RUNTIME:
Annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.
SOURCE:
Annotations are to be discarded by the compiler.
I understand the usages of RUNTIME (in order to use annotation with reflection) and CLASS (for the compiler) but I don't understand when it can be usefull to use
#Retention(RetentionPolicy.SOURCE)
Can you explain?
Things like #SuppressWarnings, #Override are annotations used by the compiler - not needed at runtime. For those RetentionPolicy.SOURCE would make sense. Also annotations can be used to generate code (look at Spring ROO) - such annotation are also not required at run time.
This answer makes perfect sense - https://stackoverflow.com/a/43910948/3009968.
You would not like to include a dependency, the desired effects of which are realized even before the code is compiled. E.g. #SuppressWarnings
You would not like to include a dependency which is used by compiler to let's say generate code but not at all required during runtime. E.g. as mentioned already in previous answer -spring roo.
RetentionPolicy.CLASS - The defined annotation will be stored in the .class file, but not available at runtime. This is the default retention policy if you do not specify any retention policy at all.
RetentionPolicy.SOURCE - The defined annotation will be ignored by the compiler when building the code. So the annotation is only available in the source code, and not in the .class files, and not at runtime too.

Is it possible to use Java annotations, to achieve a similar functionality as a preprocessor

Is it possible to create a preprocessor like functionality that is available in C and provided by Antenna. Can we use the APT tool to achieve this functionality? Are there any articles or links on similar topics?
Annotations are not meant as a tool to transform code; they just add metadata to code. You can't use annotations for conditional compilation, for example.
As Sun's tutorial on annotations says:
Annotations provide data about a program that is not part of the program itself. They have no direct effect on the operation of the code they annotate.
Wikipedia says:
When Java source code is compiled, annotations can be processed by compiler plug-ins called annotation processors. Processors can produce informational messages or create additional Java source files or resources, which in turn may be compiled and processed, but processors cannot modify the annotated code itself.
So an annotation processor plug-in is not going to be able to give you all of the functionality that the C preprocessor has.
You can perform compile-time tasks using the annotation processing framework. It's not as powerful as a preprocessor, since you can't do things like:
#RunOnlyOn(OS.Mac) public void someMethod() { ... }
Some good use cases for annotation processors are:
creating mapping files from annotated classes, e.g. create a hibernate mapping file;
creating indexes of classes which have certain annotation, e.g. create testng xml files from a source folder of test classes;
enforce compile-time constraints not usually available, e.g. having a no-arg constructor.
Please note that as of Java 6 APT is no longer needed, since all properly declared annotation processors take part in the compilation.

Categories