- A Concurrent Affair - https://www.concurrentaffair.org -

Subtyping for Annotations Discussion

After talking to Michael Ernst about subtyping for annotations and learning of strong opposition against it from Sun and Google, I looked through the archives of the JSR 308 to see if there was a discussion about subtyping that I missed or that occurred before I started reading some time in May. Maybe I still missed it, but there was really only one post by Bill Pugh, entitled “Ideas for Java annotation extension (independent of JSR-30[58])” [1], that dealt with exactly what I am doing, but also indicating that it wasn’t directly relevant to JSR-308. I think it may be useful to compare Bill’s proposals to mine.

After talking with Josh, I’ve decided to live within one design
constraint of the existing framework: for any annotation location and
annotation class, there is either zero or one matching annotations.
This forbids some things like subtyping between annotations, and
multiple instances of the same annotation on an element.

In my current implementation of xajavac [2], I have relaxed this requirement, and there can be any number of repeated annotations on a location. With subtyping, however, I believe this becomes less of an issue, since a programmer can always write an annotation that groups certain annotations together, thus in a way allowing repeated instances.

* Proposal 1: An annotation is allowed to extend/implement any number of interfaces.

In xajavac, any annotation is allowed to extend any other annotation (excluding cycles and the usual Java rules). An annotation is really just like an interface: An interface can extend other interfaces; an annotation can extend other annotations. If an interface doesn’t extend anything, its implicit superclass is Object; if an annotation doesn’t extend anything, its implicit superclass is java.lang.annotation.Annotation, as with any annotation right now. Neither an interface nor an annotation cannot extend a class; an interface can extend an annotation, but an annotation cannot extend an interface in my implementation. Classes can implement both interfaces and annotations.

You might want to use this for marker interfaces, or define an interface that defines several elements/methods, and any annotation that implements that interface would be required to implement those methods.

Note that the interface would just be a normal interface that could be implemented by classes as well (although this wouldn’t be recommended practice). interfaces can’t define default values.

This would allow you to define a standard set of element names that were used by by set of specific annotations, and allow internal API’s that worked with any annotation from that set.

I never really thought about interfaces extending annotations, although that’s clearly allowed in my implementation. The interface then can’t use as an annotation anymore, though. I don’t see much of a use here, but on the other hand, I don’t see a reason to forbid this either.

* Proposal 2 (requires proposal 1):

An annotation can define an element that returns an interface that extends java.lang.annotation.Annotation. When applying such an annotation in the source code, the value used there must be some concrete annotation that extends that interface.

Right now, the only values allowed for annotation elements are: primitives, String, Class, enums, specific annotation classes, and arrays.

This proposal would allow you to define annotations that, when applied in source code, accepted as an element value any one of a number of specific annotations. By defining the element to return a value of type Annotation, you can define an element where an arbitrary Annotation can be provided.

I think this explanation is overly complicated. I don’t think it is necessary to achieve what annotations aim to do: annotating parts of a program with data available at compile time.

For example, consider example where the Baz annotation can be applied with either a Foo or a Baz annotation as an element:

public interface AnnotationWithId extends Annotation [3] {
   int id();
public @interface Foo extends AnnotationWithId {
   int id();  // this could be omitted, since it is defined in the interface
   String [4] msg();
public @interface Bar extends AnnotationWithId {
   int id() default 0; // couldn't be omitted, since the interface can't give a default value
   String [4] date();
public @interface Baz {
   AnnotationWithId annotation(); // can supply a @Foo or @Bar here
   String [4] reason();
public Test {
   @Baz(annotation=@Foo(id=17, msg="testing"), reason="because")
      public int test() { return 17; }

In my implementation, I don’t allow annotations to extend Annotation, because that is an interface. This extension is implicit, however, as it is with any annotations currently. Other than that, everything in this example can be done with my example, as long as all the “interfaces” that are extended are annotations, and all the members are primitives, String, Class, enums, annotation classes — now including subclassing, of course — and arrays.

I considered allowing Annotation as one of the members in an annotation, in addition to String, Class, enums, annotations, and arrays, but I don’t think this is all that necessary. It would allow to have an array of any kind of annotation, but I’m not sure how useful that is. It is more likely that a programmer wants a certain kind of annotation, e.g. any kind of InvariantAnnotation, as in my invariant checker.

I suspect there may be issues I hadn’t considered, so I’d appreciate
your feedback on this.

My current thought is to not make this part of JSR-308, but directly
submit it to the JSE7 effort.

I’m starting to realize what bureaucracy is involved in making any changes to a programming language, even if it is Java, which is not standardized by an official international organization like ISO. So I guess I share Bill’s sentiment of including these changes some time in the future.

[5] [6]Share [7]