[JSR308] Proposal: Multiple Instance of same Annotation, Annotation Inheritance

Niko Matsakis niko at alum.mit.edu
Fri Oct 3 04:25:20 EDT 2008


Hello,

I would like to propose that the current annotation system be extended  
in two ways.  The first would be to allow for multiple annotations of  
the same type to be attached to the same program statement.  This is  
already mentioned in Section D.1 of the Type Annotations  
Specification.  The other would be to allow for inheritance among  
annotations.  Together, I think that these two ideas permit far more  
flexibility than the current annotation scheme.

In this e-mail, I will attempt to outline a proposal, including  
several points that remain undecided.  I would be willing to implement  
these changes, but I wanted to float a proposal and get some feedback  
before I started diving in.  In particular, I can't seem to find much  
documentation on why multiple annotations and subtyping are not  
permitted today: I assume it was out of a desire to keep the original  
proposal simple, but perhaps there are strange interactions I am not  
thinking of.

0. Problems I am trying to solve

Currently, the kind of data which can be encoded in annotations is  
quite limited.  It is generally possible to store data which has a  
fixed structure, but it is much more difficult to encode more complex  
data -- particularly hierarchical data -- without falling back to  
strings that must be manually parsed.

As a trivial example, imagine trying to encode a tree which consists  
of @Node annotations -- which may contain other nodes -- and @Leaf  
annotations -- which cannot.  Although the current annotation scheme  
allows one kind of annotation to be embedded within another, the type  
of annotation which is to be embedded must be specified.  This means  
that it would be possible to have @Nodes that contain either @Nodes or  
@Leafs, but not both.

Under this proposal, such a scheme could be written as

	public @interface Tree {}
	public @interface Node extends Tree {
		public Tree[] value();
	}
	public @interface Leaf extends Tree {}

which would allow for an annotation like:

	@Node({
		@Node({
			@Leaf
		}),
		@Node({
			@Node({
				@Leaf,
				@Leaf
			})
		})
	})

Appendix D states that proposed changes to annotations are desirable  
if "the additional changes are small, there is no better venue to add  
such an annotation, and the new syntax would permit unanticipated  
future uses."  While I believe that this proposal fulfills all three  
criteria, I think that it's primary purpose is to allow for  
unanticipated future uses.  As the success of XML, YAML, and other  
such technologies has shown, the ability to encode structured data is  
very powerful for a wide variety of uses.  Today's annotations only  
fulfill a small piece of that potential.

1. Duplicate annotations at a single location

It should be possible to place multiple instances of the same  
annotation on the same program element.  To accomodate this, the API  
for accessing annotations will need to be extended: the current API  
can only return a single annotation instance for a given annotation  
type.  I prefer the second alternative described in Appendix D.

Under this proposal, the existing AnnotatedElement interface would be  
modified slightly.  The current method

	<T extends Annotation> T getAnnotation(Class<T> annotationClass)

would be defined to return the first annotation of the given class (or  
a subtype, see next section), or null if no such annotations are  
present.  A new method:

	<T extends Annotation> T[] getAnnotations(Class<T> annotationClass)

would return an array containing all annotations in a given class.   
The array will have length 0 if no such annotations exist.

The arrays returned by the methods getAnnotations(Class),  
getAnnotations(), and getDeclaredAnnotations() will preserve the  
ordering of the annotations as they were declared in the  
original .java source file.

In some cases, it might not be desirable to allow multiple annotations  
of the same class to co-exist.  In this case, a new meta-annotation  
can be used:

	public @interface Repeatable {
		public boolean value() = true;
	}

which indicates explicitly whether or not an annotation can be used  
multiple times on a single element.  For backwards compatibility, the  
compiler will ensure that annotations that are not meta-annotated with  
@Repeatable cannot be used multiple times.

2. Subtyping among annotations

Currently, all annotation types are direct subtypes of  
java.lang.Annotation.  I propose to allow annotations to have a  
subtyping relationship.  To avoid complications, only single  
inheritance would be permitted.

Sub-annotation types may define new properties not defined in the  
supertype.  They may also redefine existing properties from the  
supertype so long as the new type of the property is a subtype of the  
type defined in the supertype (i.e., return type covariance is  
supported, as in other Java overrides).

3. Changes required to the implementation

The compiler would have to be changed to allow for multiple  
annotations and to check the new error conditions (multiple  
annotations without a @Repeatable attribute, errors in subtyping).   
The java.lang API must be updated with the new getAnnotations() method.

4. Backwards compatibility issues

This section addresses some possible problems that might arise when  
attempting to use old code -- either .java or .class files -- with a  
newer runtime/compiler.

In terms of source compatibility, there are no backwards compatibility  
problems with this proposal.  Old code should continue to work exactly  
as it does today.  Only annotations which have the @Repeatable  
attribute can be repeated, so those existing cases where only a single  
instance of an annotation is semantically correct will continue to  
cause errors as they do today.  Likewise, code written under the  
assumption that only a single instance of an annotation is permitted  
will continue to operate correctly.  If all annotation types could be  
repeated by default, such code would still function, but it would only  
process the first instance of any given annotation type.

In terms of binary compatibility, the major danger comes from the  
changes to the AnnotatedElement interface which add an additional  
method.  This could be a problem if there are other implementors of  
this interface outside of the Java standard library.  I consider that  
to be unlikely: however, it would also be an option to create a second  
interface, AnnotatedElement2, which extended the original interface  
and contains the new methods.

5. Possible changes

Of course, all the names of interfaces and methods in this proposal  
could be changed.  In addition, it might be desirable to allow any  
annotation to be repeated by default, although this might allow some  
annotations to be abused, and/or break old source code that expected  
only one instance of a given annotation type.  It is also not  
necessary to allow both subtyping and multiple annotations at a given  
location, though it is my belief that combining the two allows for  
more benefit than either change individually.


regards,
Niko Matsakis



More information about the JSR308 mailing list