2012-04-10 20 views
58

He una clase padre Parent y una clase hija Child, que se define así:¿Cómo funcionan las anotaciones de método Java junto con la anulación de método?

class Parent { 
    @MyAnnotation("hello") 
    void foo() { 
     // implementation irrelevant 
    } 
} 
class Child { 
    @Override 
    foo() { 
     // implementation irrelevant 
    } 
} 

Si obtengo una referencia Method-Child::foo, se childFoo.getAnnotation(MyAnnotation.class) dame @MyAnnotation? ¿O será null?

Estoy interesado más en general en cómo o si la anotación funciona con la herencia de Java.

Respuesta

60

una copia literal de http://www.eclipse.org/aspectj/doc/released/adk15notebook/annotations.html#annotation-inheritance:

anotación Herencia

Es importante entender las reglas relativas a la herencia de anotaciones, ya que éstos influyen en unirse a punto de coincidencia en base a la presencia o ausencia de anotaciones

Por defecto, las anotaciones no se heredan. Dado el siguiente programa

 @MyAnnotation 
     class Super { 
      @Oneway public void foo() {} 
     } 

     class Sub extends Super { 
      public void foo() {} 
     } 

Entonces Sub no tiene la anotación MyAnnotation y Sub.foo() no es un método @Oneway, a pesar del hecho de que se anula Super.foo() que es.

Si un tipo de anotación tiene la meta-anotación @Inherited, entonces una anotación de ese tipo en una clase provocará que la anotación sea heredada por las subclases. Por lo tanto, en el ejemplo anterior, si el tipo MyAnnotation tenía el atributo @Inherited, entonces Sub tendría la anotación MyAnnotation.

@Inherited las anotaciones no se heredan cuando se utilizan para anotar cualquier cosa que no sea un tipo. Un tipo que implementa una o más interfaces nunca hereda ninguna anotación de las interfaces que implementa.

+34

Además, trutheality, * I * buscado anteriormente pregunté, y se me ocurrió esta página. Felicidades, ahora eres parte de esos resultados de búsqueda. Es por eso que este sitio web está aquí. :) Además, tu respuesta es mucho más concisa que consultar ese documento. – Tustin2121

+1

Una pregunta para ampliar esto ... si un marco encuentra el método basado en la anotación y luego lo invoca, ¿qué versión del método se invoca? El método de la clase infantil debe anular al padre, pero ¿se respeta eso con la invocación reflexiva? –

9

Ya encontró su respuesta: no hay ninguna disposición para la herencia de anotación de método en el JDK.

Pero subir la cadena de super-clase en busca de métodos anotados también es fácil de implementar:

/** 
* Climbs the super-class chain to find the first method with the given signature which is 
* annotated with the given annotation. 
* 
* @return A method of the requested signature, applicable to all instances of the given 
*   class, and annotated with the required annotation 
* @throws NoSuchMethodException If no method was found that matches this description 
*/ 
public Method getAnnotatedMethod(Class<? extends Annotation> annotation, 
           Class c, String methodName, Class... parameterTypes) 
     throws NoSuchMethodException { 

    Method method = c.getMethod(methodName, parameterTypes); 
    if (method.isAnnotationPresent(annotation)) { 
     return method; 
    } 

    return getAnnotatedMethod(annotation, c.getSuperclass(), methodName, parameterTypes); 
} 
+0

Esto no prevé una situación donde la anotación solicitada no se encuentra en ninguna de las implementaciones de superclase; en este caso arrojará 'NoSuchMethodException', aunque' null' sería un valor de retorno legítimo ... –

+0

@UriAgassi Eso es por diseño. Creo que JavaDoc lo deja en claro. – Saintali

+0

lo único que el javadoc deja en claro es que se lanza una 'NoSuchMethodException'. Debería lanzar una excepción de este tipo cuando _el método no exista_, no cuando _no se encuentre la anotación _... no sea compatible con la implementación original (que devolverá 'null' en tal caso) –

2

Mientras que la respuesta a la pregunta hecha es que Java de Method.getAnnotation() no tiene en cuenta métodos sobrescritos, a veces es útil para encontrar estas anotaciones. He aquí una versión más completa de la respuesta de Saintali que actualmente estoy usando:

public static <A extends Annotation> A getInheritedAnnotation(
    Class<A> annotationClass, AnnotatedElement element) 
{ 
    A annotation = element.getAnnotation(annotationClass); 
    if (annotation == null && element instanceof Method) 
     annotation = getOverriddenAnnotation(annotationClass, (Method) element); 
    return annotation; 
} 

private static <A extends Annotation> A getOverriddenAnnotation(
    Class<A> annotationClass, Method method) 
{ 
    final Class<?> methodClass = method.getDeclaringClass(); 
    final String name = method.getName(); 
    final Class<?>[] params = method.getParameterTypes(); 

    // prioritize all superclasses over all interfaces 
    final Class<?> superclass = methodClass.getSuperclass(); 
    if (superclass != null) 
    { 
     final A annotation = 
      getOverriddenAnnotationFrom(annotationClass, superclass, name, params); 
     if (annotation != null) 
      return annotation; 
    } 

    // depth-first search over interface hierarchy 
    for (final Class<?> intf : methodClass.getInterfaces()) 
    { 
     final A annotation = 
      getOverriddenAnnotationFrom(annotationClass, intf, name, params); 
     if (annotation != null) 
      return annotation; 
    } 

    return null; 
} 

private static <A extends Annotation> A getOverriddenAnnotationFrom(
    Class<A> annotationClass, Class<?> searchClass, String name, Class<?>[] params) 
{ 
    try 
    { 
     final Method method = searchClass.getMethod(name, params); 
     final A annotation = method.getAnnotation(annotationClass); 
     if (annotation != null) 
      return annotation; 
     return getOverriddenAnnotation(annotationClass, method); 
    } 
    catch (final NoSuchMethodException e) 
    { 
     return null; 
    } 
} 
Cuestiones relacionadas