src/share/classes/java/lang/reflect/AnnotatedElement.java

Print this page

        

@@ -25,10 +25,19 @@
 
 package java.lang.reflect;
 
 import java.lang.annotation.Annotation;
 import java.lang.annotation.AnnotationFormatError;
+import java.lang.annotation.Repeatable;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import sun.reflect.annotation.AnnotationSupport;
+import sun.reflect.annotation.AnnotationType;
 
 /**
  * Represents an annotated element of the program currently running in this
  * VM.  This interface allows annotations to be read reflectively.  All
  * annotations returned by methods in this interface are immutable and

@@ -220,36 +229,86 @@
      * annotation.
      *
      * The caller of this method is free to modify the returned array; it will
      * have no effect on the arrays returned to other callers.
      *
+     * @implSpec The default implementation first calls {@link
+     * #getDeclaredAnnotationsByType(Class)} passing {@code
+     * annotationClass} as the argument. If the returned array has
+     * length greater than zero, the array is returned. If the returned
+     * array is zero-length and this {@code AnnotatedElement} is a
+     * class and the argument type is an inheritable annotation type,
+     * and the superclass of this {@code AnnotatedElement} is non-null,
+     * then the returned result is the result of calling {@link 
+     * #getAnnotationsByType(Class)} on the superclass with {@code
+     * annotationClass} as the argument. Otherwise, a zero-length
+     * array is returned.
+     *
      * @param <T> the type of the annotation to query for and return if present
      * @param annotationClass the Class object corresponding to the
      *        annotation type
      * @return all this element's annotations for the specified annotation type if
      *     associated with this element, else an array of length zero
      * @throws NullPointerException if the given annotation class is null
      * @since 1.8
      */
-    <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass);
+    default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {
+         /*
+          * Definition of associated: directly or indirectly present OR
+          * neither directly nor indirectly present AND the element is
+          * a Class, the annotation type is inheritable, and the
+          * annotation type is associated with the superclass of the
+          * element.
+          */
+         T[] result = getDeclaredAnnotationsByType(annotationClass);
+
+         if (result.length == 0 && // Neither directly nor indirectly present
+             this instanceof Class && // the element is a class
+             AnnotationType.getInstance(annotationClass).isInherited()) { // Inheritable
+             Class<?> superClass = ((Class<?>) this).getSuperclass();
+             if (superClass != null) {
+                 // Determine if the annotation is associated with the
+                 // superclass
+                 result = superClass.getAnnotationsByType(annotationClass);
+             }
+         }
+
+         return result;
+     }
 
     /**
      * Returns this element's annotation for the specified type if
      * such an annotation is <em>directly present</em>, else null.
      *
      * This method ignores inherited annotations. (Returns null if no
      * annotations are directly present on this element.)
      *
+     * @implSpec The default implementation first performs a null check
+     * and then loops over the results of {@link
+     * #getDeclaredAnnotations} returning the first annotation whose
+     * annotation type matches the argument type.
+     *
      * @param <T> the type of the annotation to query for and return if directly present
      * @param annotationClass the Class object corresponding to the
      *        annotation type
      * @return this element's annotation for the specified annotation type if
      *     directly present on this element, else null
      * @throws NullPointerException if the given annotation class is null
      * @since 1.8
      */
-    <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass);
+    default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {
+         Objects.requireNonNull(annotationClass);
+         // Loop over all directly-present annotations looking for a matching one
+         for (Annotation annotation : getDeclaredAnnotations()) {
+             if (annotationClass.equals(annotation.annotationType())) {
+                 // More robust to do a dynamic cast at runtime instead
+                 // of compile-time only.
+                 return annotationClass.cast(annotation);
+             }
+         }
+         return null; 
+     }
 
     /**
      * Returns this element's annotation(s) for the specified type if
      * such annotations are either <em>directly present</em> or
      * <em>indirectly present</em>. This method ignores inherited

@@ -266,20 +325,45 @@
      * through" a container annotation if one is present.
      *
      * The caller of this method is free to modify the returned array; it will
      * have no effect on the arrays returned to other callers.
      *
+     * @implSpec The default implementation may call {@link
+     * #getDeclaredAnnotation(Class)} one or more times to find a
+     * directly present annotation and, if the annotation type is
+     * repeatable, to find a container annotation. If annotations of
+     * the annotation type {@code annotationClass} are found to be both
+     * directly and indirectly present, then {@link
+     * #getDeclaredAnnotations()} will get called to determine the
+     * order of the elements in the returned array.
+     *
+     * <p>Alternatively, the default implementation may call {@link
+     * #getDeclaredAnnotations()} a single time and the returned array
+     * examined for both directly and indirectly present
+     * annotations. The results of calling {@link
+     * #getDeclaredAnnotations()} are assumed to be consistent with the
+     * results of calling {@link #getDeclaredAnnotation(Class)}.
+     *
      * @param <T> the type of the annotation to query for and return
      * if directly or indirectly present
      * @param annotationClass the Class object corresponding to the
      *        annotation type
      * @return all this element's annotations for the specified annotation type if
      *     directly or indirectly present on this element, else an array of length zero
      * @throws NullPointerException if the given annotation class is null
      * @since 1.8
      */
-    <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass);
+    default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) {
+        Objects.requireNonNull(annotationClass);
+        return AnnotationSupport.
+            getDirectlyAndIndirectlyPresent(Arrays.stream(getDeclaredAnnotations()).
+                                            collect(Collectors.toMap(Annotation::annotationType,
+                                                                     Function.identity(),
+                                                                     ((first,second) -> first),
+                                                                     LinkedHashMap::new)),
+                                            annotationClass);
+    }
 
     /**
      * Returns annotations that are <em>directly present</em> on this element.
      * This method ignores inherited annotations.
      *