A simpler model for repeating annotations

Remi Forax forax at univ-mlv.fr
Fri Dec 28 14:45:18 PST 2012


On 12/28/2012 09:17 PM, Alex Buckley wrote:
> Feedback from Java EE architects indicates a strong preference for 
> 100% behavioral compatibility for legacy core reflection methods.
>
> Summarizing Mike Keith's comments: 1) getAnnotation(Class) must not 
> return one @Foo annotation if multiple @Foo annotations were present 
> in source, and 2) getAnnotations() must not return multiple @Foo 
> annotations if they present in source.
>
> These requirements imply keeping containment at the heart of 
> repeatability. Why? Assume there were no containers, and you could 
> simply write:
>
>   @Repeatable @interface Foo {}
>
> such that @Foo @Foo on class A is compiled directly into A.class. This 
> scheme would impose high complexity on legacy core reflection methods. 
> getAnnotation(Foo.class) would have to read _all_ annotations 
> (declared and inherited) to detect multiple @Foo annotations then 
> return null or throw an exception. (Remember, returning one of the 
> multiple annotations is "wrong".) getAnnotations() would have similar 
> trouble deciding what to return if multiple @Foo annotations are 
> found. In essence, the legacy core reflection methods are morally 
> incompatible with uncontained multiple annotations. The solution is to 
> compile multiple annotations with containers, and have the legacy core 
> reflection methods behave identically to Java SE 7 (no look through).
>
> To support new reflective clients who understand repeating 
> annotations, we can keep the previously-proposed getAnnotations(Class) 
> which looks through containers. That is, getAnnotations(Foo.class) 
> returns @Foo @Foo because it a) detects that Foo.class is a repeatable 
> annotation type with a containing annotation type of FooContainer, 
> then b) looks through any @FooContainer present.
>
> It is clear that a repeatable annotation type must still nominate its 
> containing annotation type, but the reverse is no longer true. That 
> is, the declaration of FooContainer no longer needs to say 
> @ContainerFor(Foo.class). This is a direct consequence of dropping 
> look through for the legacy core reflection methods. Also, the 
> difficult question about calling getAnnotations() on a subclass - 
> should it look through an inherited container, or return it? - goes 
> away, as normal overriding policy gives good answers.
>
> Here's a simple model that Joe, Joel, and I are happy with:
>
> - @Repeatable(FooContainer.class) on @interface Foo {} causes
>   @Foo @Foo in source to be containerized in the class file.
>   (Rename @ContainedBy to @Repeatable.)
>
> - The rules for a well-formed relationship between Foo and
>   FooContainer remain.
>
> - Core reflection's legacy methods return what's in the class file:
>   - get[Declared]Annotation(Foo.class) returns nothing, like today.
>   - get[Declared]Annotations() returns @FooContainer, like today.
>
> - Core reflection's new typed methods look through containers:
>   - get[Declared]Annotations(Foo.class) returns @Foo @Foo.
>
> - An annotation writer may manually rewrite @FooContainer(...) as
>   @Foo @Foo, and it will be containerized with perfect compatibility.
>
> - Language model's legacy methods follow core reflection:
>   - Element.getAnnotation(Foo.class) returns nothing, like today.
>   - Element[s].get[All]AnnotationMirrors() returns @FooContainer, like 
> today
>
> - Language model's new typed method looks through:
>   - Element.getAnnotations(Foo.class) returns @Foo @Foo.
>   - We may add Element.getAnnotationMirrors(TypeElement) in future.
>
> I think the reduced semantic burden for an annotation type owner (no 
> more @ContainedFor) and the stronger compatibility story for an 
> annotation reader (no look through for legacy methods) are real 
> improvements. We will specify and implement the model over the next 
> month.
>
> Alex

This spec is way better,
to be sure, do you agree that getDeclaredAnnotations can be written like 
this:

A[] getDeclaredAnnotations(Class<A> annotationClass) {
   Repeatable repeatable = 
annotationClass.getDeclaredAnnotation(Repetable.class);
   if (repeatable != null) {
     Annotation container = getDeclaredAnnotation(repeatable.value());
     if (container != null) {   // if there is a container annotation, 
no single annotation can be present ?
       return (A[])callValuesByReflection(container);
     }
   }
   A annotation = getDeclaredAnnotation(annotationClass);
   if (annotation == null)
     return (A[])Arrays.newInstance(annotationClass, 0)
   A[] annotations = (A[])Arrays.newInstance(annotationClass, 1);
   annotations[0] = annotation;
   return annotations;
}

cheers,
Rémi




More information about the enhanced-metadata-spec-discuss mailing list