I noticed a discrepancy between Eclipse's compiler and javac while using repeating annotations. The repeating annotation and its container were in the same package, but the former was declared public, while the latter remained package-private. Eclipse had no problem with the arrangement, even though the repeating annotation was referenced in another package. javac, on the other hand, refused to compile, saying
value() in [container] is defined in an inaccessible class or interface
My question is, which one is correct? I couldn't find any rule about this in the JLS. Does that mean it's open to interpretation? Or is there a bug in one of the compilers?
There is no statement regarding accessibility but the specification makes it clear that it is possible that a repeatable annotation may be restricted to be repeatable at certain locations only, due to the way how the containing annotation has been declared.
JLS §9.6.3
…
T is applicable to at least the same kinds of program element as TC (§9.6.4.1). Specifically, if the kinds of program element where T is applicable are denoted by the set m1, and the kinds of program element where TC is applicable are denoted by the set m2, then each kind in m2 must occur in m1, …
This clause implements the policy that an annotation type may be repeatable on only some of the kinds of program element where it is applicable
This is also backed with an example:
Example 9.6.3-2. Restricting Where Annotations May Repeat
An annotation whose type declaration indicates a target of java.lang.annotation.ElementType.TYPE can appear in at least as many locations as an annotation whose type declaration indicates a target of java.lang.annotation.ElementType.ANNOTATION_TYPE. For example, given the following declarations of repeatable and containing annotation types:
#Target(ElementType.TYPE)
#Repeatable(FooContainer.class)
#interface Foo {}
#Target(ElementType.ANNOTATION_TYPE)
#Interface FooContainer {
Foo[] value();
}
#Foo can appear on any type declaration while #FooContainer can appear on only annotation type declarations. Therefore, the following annotation type declaration is legal:
#Foo #Foo
#interface X {}
while the following interface declaration is illegal:
#Foo #Foo
interface X {}
While this is discussing restrictions imposed by #Target rather than accessibility modifiers, it describes a “spirit” that can be applied to the latter as well. The intention clearly seems to be that a repeatable annotation can only be repeated if the properties of the specified container annotation allow it, otherwise the annotation still may be used, but only with a single occurrence.
It’s not like the properties of a repeatable annotation were capable of overriding the properties of the container annotation type. This is in line with the behavior at all other places; even if there is no reference to the inaccessible annotation type in the source code, compiling the repeated annotation would create a reference in the class file and a class file may not contain symbolic references to inaccessible classes and there shouldn’t be an exception only for container annotations.
Related
Are there such things as Java annotations that aren't tied to any class, method, field, etc.?
Like just writing
#MyAnnotation(someParameter=value, ...)
by itself, and it generates code.
It seems like ExecutableType might define what kinds of "elements" an annotation can annotate, but I'm not sure. If that's true, then ExecutableType derives from TypeMirror, one of whose members are NoType. So maybe it's possible? But I cannot find an example of this.
You cannot have a stand-alone annotation in Java.
Annotations can be applied to different things, for example: types, methods, fields, local variables, packages, method parameters and also on annotation definitions.
One annotation that is meant to be used on annotation definitions (therefore it's called a "meta-annotation") is #Target, which you use to indicate on what things the annotation you are defining is allowed to be used. You do this by specifying one or more element types as an argument to the #Target annotation - see the API docs of java.lang.annotation.ElementType.
The Java Language Specification paragraph 9.6.4.1 explains what annotations can be used on in more detail.
Consider the following code:
A.java:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
#interface A{}
C.java:
import java.util.*;
#A public class C {
public static void main(String[] args){
System.out.println(Arrays.toString(C.class.getAnnotations()));
}
}
Compiling and running works as expected:
$ javac *.java
$ java -cp . C
[#A()]
But then consider this:
$ rm A.class
$ java -cp . C
[]
I would've expected it to throw a ClassNotFoundException, since #A is missing. But instead, it silently drops the annotation.
Is this behaviour documented in the JLS somewhere, or is it a quirk of Sun's JVM? What's the rationale for it?
It seems convenient for things like javax.annotation.Nonnull (which seems like it should've been #Retention(CLASS) anyway), but for many other annotations it seems like it could cause various bad things to happen at runtime.
In the earlier public drafts for JSR-175 (annotations), it was discussed if the compiler and runtime should ignore unknown annotations, to provide a looser coupling between the usage and declaration of annotations. A specific example was the use of applications server specific annotations on an EJB to control the deployment configuration. If the same bean should be deployed on a different application server, it would have been convenient if the runtime simply ignored the unknown annotations instead of raising a NoClassDefFoundError.
Even if the wording is a little bit vague, I assume that the behaviour you are seeing is specified in JLS 13.5.7: "... removing annotations has no effect on the correct linkage of the binary representations of programs in the Java programming language." I interpret this as if annotations are removed (not available at runtime), the program should still link and run and that this implies that the unknown annotations are simply ignored when accessed through reflection.
The first release of Sun's JDK 5 did not implement this correctly, but it was fixed in 1.5.0_06. You can find the relevant bug 6322301 in the bug database, but it does not point to any specifications except claiming that "according to the JSR-175 spec lead, unknown annotations must be ignored by getAnnotations".
Quoting the JLS:
9.6.1.2 Retention Annotations may be present only in the source code, or
they may be present in the binary form
of a class or interface. An annotation
that is present in the binary may or
may not be available at run-time via
the reflective libraries of the Java
platform.
The annotation type
annotation.Retention is used to choose
among the above possibilities. If an
annotation a corresponds to a type T,
and T has a (meta-)annotation m that
corresponds to annotation.Retention,
then:
If m has an element whose value is annotation.RetentionPolicy.SOURCE,
then a Java compiler must ensure that
a is not present in the binary
representation of the class or
interface in which a appears.
If m has an element whose value is annotation.RetentionPolicy.CLASS, or
annotation.RetentionPolicy.RUNTIME a
Java compiler must ensure that a is
represented in the binary
representation of the class or
interface in which a appears, unless m
annotates a local variable
declaration. An annotation on a local
variable declaration is never retained
in the binary representation.
If T does not have a (meta-)annotation
m that corresponds to
annotation.Retention, then a Java
compiler must treat T as if it does
have such a meta-annotation m with an
element whose value is
annotation.RetentionPolicy.CLASS.
So RetentionPolicy.RUNTIME ensures that the annotation is compiled into the binary but an annotation present in the binary doesn't have to be available at runtime
if you actually have code that reads #A and does something with it, the code has a dependency on class A, and it will throw ClassNotFoundException.
if not, i.e. no code cares specificly about #A, then it's arguable that #A doesn't really matter.
Section 4.7.16 of the JVM specification includes a description of "RuntimeVisibleAnnotations". I am wondering what can cause an attribute to be included in this attributes table, is this only by applying #Retention(RetentionPolicy.RUNTIME) on an attribute? Conversely, for "RuntimeInvisibleAnnotations" (see further in 4.7.17) is this #Retention(RetentionPolicy.CLASS) only or is it also #Retention(RetentionPolicy.SOURCE) ?
Compiling information from the JVM and JLS specifications gives us the following picture:
Annotations meta-annotated with the #Retention whose value is RetentionPolicy.SOURCE must not be present in the binary representation of the class or interface in which they appear, i.e. they are not to be recorded in the class file at all.
Annotations with the RetentionPolicy.CLASS must be represented in the binary representation of the class or interface in which they appear, unless they annotate a local variable declaration. An annotation on a local variable declaration is never retained in the binary representation.
So this is what the RuntimeInvisibleAnnotations attribute is designed for.
They need not be retained by the VM at run time, unless the Java Virtual Machine has been instructed to retain these annotations via some implementation-specific mechanism such as a command line flag.
Annotations with the RetentionPolicy.RUNTIME are to be recorded in the class file by the compiler and must be available at run time via reflection libraries. This is for the RuntimeVisibleAnnotations attribute.
I don't know if the question I am asking is really stupid. But here it is:
I would like to write a custom annotation which should be applicable to a specific type. For example, if I have a class A, then I would like to have an annotation that can be applied on objects of A.
Something like this:
#Target({ElementType.FIELD, //WHAT_ELSE_HERE_?})
public #interface MyAnnotation {
String attribute1();
}
public class X {
#MyAnnotation (attribute1="...") //SHOULDN'T BE POSSIBLE
String str;
#MyAnnotation (attribute1="..") //PERFECTLY VALID
A aObj1;
#MyAnnotation (attribute1="...") //SHOULDN'T BE POSSIBLE
B bObj1;
}
Is that possible at all?
Not possible. #Target uses ElementType[], and ElementType is an enum, so you can't modify it. It does not contain a consideration for only specific field types.
You can, however, discard the annotation at runtime, or raise runtime exceptions about it.
That is not possible in Java.
But you have an option to write your own annotation processor if you want to check the correctness of the annotations before runtime.
Annotation processing is a hook in the compile process, to analyse the
source code for user defined annotations and handle then (by producing
compiler errors, compiler warning, emmiting source code, byte code
..).
A basic tutorial on Annotation Processing.
Suppose I had an interface with some annotation(s), for example:
#SpecialClass
public interface IFoo { /* ... */ }
And suppose I make a class that implements the interface:
public class Foo implements IFoo { /* ... */ }
Is it possible for class Foo to somehow "inherit" or automatically copy all or some of the annotations from IFoo and its members (e.g. automagically annotate Foo as #SpecialClass, etc.)?
This would be convenient for implementing web service classes (e.g. those generated by the JAX-WS "wsimport" tool) by just implementing their annotated interfaces without explicitly having to copy the interface annotations to the implementing class (e.g. javax.jws.WebService, javax.xml.ws.RequestWrapper, etc).
EDIT: I'm leaving this answer here for general information and future readers, but Andreas pointed out an important bit of the Javadoc which I'd missed:
Note that this meta-annotation type has no effect if the annotated type is used to annotate anything other than a class. Note also that this meta-annotation only causes annotations to be inherited from superclasses; annotations on implemented interfaces have no effect.
In other words, it wouldn't help in this situation. Also it's only useful if you have control over the annotation itself, of course.
I suspect the real answer is that you simply have to apply the annotation everywhere. If you're worried about forgetting one, you might want to write a unit test which finds all your classes (easier said than done, I realise) and checks that the annotation is present for all classes implementing the given interface.
Have you tried applying the Inherited annotation to the SpecialClass annotation itself?
Indicates that an annotation type is automatically inherited. If an Inherited meta-annotation is present on an annotation type declaration, and the user queries the annotation type on a class declaration, and the class declaration has no annotation for this type, then the class's superclass will automatically be queried for the annotation type. This process will be repeated until an annotation for this type is found, or the top of the class hierarchy (Object) is reached. If no superclass has an annotation for this type, then the query will indicate that the class in question has no such annotation.
That certainly sounds like exactly what you want.