19

I'm well aware about the meaning of the RetentionPolicy and know what they do and when it seems to make sense to use them. For my own annotation, I exactly know if they're needed at runtime, in class files, or just for the compilation. However, with any annotation defined in a library, you can IMHO never be sure.

For example, javax.annotation.Generated is meant to mark generated code, but it's rarely useful. As there are AFAIK more tools working on the bytecode than tools working with the source, the information disappears just before it could be used.

As annotations absent at runtime don't throw ClassNotFoundException (unlike e.g., missing interfaces), using RetentionPolicy.RUNTIME seems to cause no harm. Or am I wrong?

Or was the saving of a few bytes the reason for using different Retentions? To me it seems to cause too many problems to be worth it. What am I missing?

Community
  • 1
  • 1
maaartinus
  • 44,714
  • 32
  • 161
  • 320

3 Answers3

15

The inspiration for Java Annotations occurred before 2002, around the transition from Java 1.3 to Java 1.4. A high spec desktop in those days was a Pentium 4 at about 2.5GHz or an Athlon XP+ at around 2GHz, RAM would be 256 or 512MB. Eg a review here.

The problem was how to store and retrieve metadata about the code. The typical solution was by using XML files that were not type-checked or directly linked to the source code. Others were already informally extending JavaDoc (the source code and extension API was present in the JDK) for code generation tools. The solution, Annotations, is hack (a pretty darn good hack) that extended Javadoc and the JLS Class specification.

It's clear the original authors were worried about performance (and in 2002, Java was still relatively slow, reflection very slow and the Java runtime was an enormous memory hog; some things never change). This is from the introduction to JSR-175:

Since many annotations will be used only by development tools such as stub generators, it makes little sense to retain all annotations at run time; doing so could increase run-time memory-footprint and harm performance. There are, however, some annotation types that are useful at run time, and some that are useful in tools that only have access to class files (not source files). Therefore, certain annotations are stored by the compiler in class file attributes (JVMS 4.7), and some of these annotations are then made available for inspection at runtime via new reflective APIs.

Their solution to the problem was to divide the problem into three use cases:

VI. Reading annotations

Annotation consumers may be divided into three groups:

a. "Introspectors" - Programs that query runtime-visible annotations of their own program elements. These programs will load both annotated classes and annotation interfaces into the virtual machine. (By runtime-visible, we mean annotations whose retention policy is RUNTIME.)

b. "Specific Tools" - Programs that query known annotation types of arbitrary external programs. Stub generators, for example, fall into this category. These programs will read annotated classes without loading them into the virtual machine, but will load annotation interfaces.

c. "General Tools" - Programs that query arbitrary annotations of arbitrary external programs (such as compilers, documentation generators, and class browsers). These programs will load neither annotated classes nor annotation interfaces into the virtual machine. Such programs are said to operate "at arm's length."

This allowed the (at that time) significant use cases of "specific tools" and "general tools" as defined above to do their thing without creating a burden on the runtime; for these tools, annotations could either be SOURCE or CLASS. Only annotations that were needed at runtime (from the above, it's clear this is was considered the minority use case) would be loaded and retained in the JVM.

So, yes, the retention policy was put in place to save bytes and runtime overhead. While this looks quaint now, 2002 was a different world and memory and performance were very real concerns. Now we have 10x performance and memory, you can safely use RUNTIME retention without concern.

Andrew Alcock
  • 19,401
  • 4
  • 42
  • 60
  • 1
    I wonder why Google's [AutoValue](https://github.com/google/auto/blob/master/value/src/main/java/com/google/auto/value/AutoValue.java) doesn't have runtime retention. I personally would find it useful for Jackson deserialization. – shmosel May 15 '16 at 04:58
2

For example, javax.annotation.Generated is meant to mark generated code, but it's rarely useful. As there are AFAIK more tools working on the bytecode than tools working with the source, the information disappears just before it could be used.

Take a look at source code editor, Android Studio derived from JetBrains and many other IDE, need to work on source code and it provides all great editing experience just because of compile time annotations.

While the class is being edited (not yet compiled), editor can store and process annotations.

Example:

@SuppressWarnings let's you suppress warnings, how else could you do it? C# allows you to define #PRAGMA , #IF, some sort of conditional compilation. None of conditional compilation information is stored in the compiled output.

@Override allows Java compiler to check if base class has a method to override or not, if you define a new method with an erroneous parameters, java compiler will compile class with new method with overload, but in presence of @Override java compiler will give you an error that signature does not match correctly to override the method.

@GeneratedCode allows IDE to skip classes and members to be displayed when you search using "Find and Replace", and it lets you operate the IDE only on your code and not the generated one. Have you seen R.* for resources in Android, these generated classes are hidden in Android Studio but they do provide useful code completion lists.

Similarly many such annotations allow you to do code analysis, write unit tests etc and do more work with it before compilation.

Here is more

Many ORM frameworks use compile time annotations and generates useful extra classes used for typed queries and other helper classes to create tables and maintain schema.

Conclusion

In example shown above, it is clear that all three and many such annotations will unnecessary add so many bytes which are totally useless at runtime.

Java had two options, one was adding some sort of compile time annotation using #IF etc directives used in c based language. Which would require, new syntax and new editing experience etc, and another was to create Retention. It was nice move to create Retention without breaking syntax.

Akash Kava
  • 39,066
  • 20
  • 121
  • 167
  • Agreed about `@SuppressWarnings` and `@Override`, but `@Generated` is an example showing that it's needed in the classfiles because of [this](https://github.com/rzwitserloot/lombok/issues/1014) and [this](https://github.com/rzwitserloot/lombok/issues/624) tools. We could agree that a [different annotation](https://projectlombok.org/api/lombok/Generated.html) is needed and maybe using standard annotations was really a bad idea. – maaartinus May 18 '16 at 17:29
  • @maaartinus what do you mean by standard annotations? you mean like different syntax, directives like ones in c? – Akash Kava May 18 '16 at 17:33
  • I mean things like [`javax.annotation.Generated`](https://docs.oracle.com/javase/7/docs/api/javax/annotation/Generated.html). It should be documented with "Do not use it, unless you're perfectly sure, you won't need it in class files. And you can never be sure." – maaartinus May 18 '16 at 17:41
1

The primary purpose of annotations is carrying meta data for a compilation unit. Most of the standard annotations are clearly expressing meta information that helps with code development and compilation (by specifying properties that are validatable by IDE or compiler).

Annotations are not designed for modifying the runtime semantics of the language. So, whether an annotation is available or not at runtime does not by itself change execution. (Of course, if you activly use meta information for adjusting your implementation behaviour then anything is possible.)

If within a library jar, somewhere an annotation is marked as RetentionPolicy.RUNTIME it is clearly expected that accessing the annotation from runtime (using reflection) is useful to later users.
If at the same time the implementation of the annotation is from another library, then such expectation is either not warranted or is due to a specific purpose of this annotation that might only be helpful for certain use cases. (And constructing different jar versions just for different retention settings for sure is not appropriate.)

Thus, if a developper is marking an annotation as RetentionPolicy.RUNTIME there is a clear usecase in mind where runtime access is expected. Whether the annotation implementation is then provided with the same jar or a different one might be independent from the use case (e.g. based on other structuring criteria). In any case, if you are intending to benefit from this use case, you will have this annotation library within your class path (as you might also need other components) and all is fine. if you do not apply to this use case, then you would not be affected from the annotation implementation being missing.

Rewording along your question:

using RUNTIMEretention does not do any harm to the program, besides cluttering (bytecode) executables with dead information. Using RUNTIME retention only where runtime use of the meta information is expected (and considered useful) adds to code quality (in terms of maintainability and understandabilty).

rpy
  • 3,953
  • 2
  • 20
  • 31
  • Your answer contradicts the top answer. Anyway, the problem seems to be people believing they should use standard annotation, when they actually need another one because of Retention. – maaartinus May 18 '16 at 17:32
  • @maaartinus: Granted, removed misleading statement. And I agreee, using `Retention.RUNTIME` with the common pre-defined annotations usually is not well grounded. With user defined ones this is a different story. And any of those should have a clear specification of whether RUNTIME retention is being expected (and under what circumstances). – rpy May 18 '16 at 17:55