2012-04-10 14 views
58

Ho una classe genitore Parent e una classe figlia Child, definito così:In che modo le annotazioni del metodo Java funzionano in combinazione con l'override del metodo?

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

Se posso ottenere un Method riferimento a Child::foo, sarà childFoo.getAnnotation(MyAnnotation.class) dammi @MyAnnotation? O sarà null?

Sono interessato in generale a come o se l'annotazione funziona con l'ereditarietà di Java.

risposta

60

copiato letteralmente da http://www.eclipse.org/aspectj/doc/released/adk15notebook/annotations.html#annotation-inheritance:

nota Inheritance

È importante comprendere le norme relative alla successione di annotazioni, poiché questi hanno un impatto sul join punto di coincidenza in base alla presenza o assenza di annotazioni.

Per impostazione predefinita le annotazioni non vengono ereditate. Dato il seguente programma

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

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

Poi Sub non ha la MyAnnotation annotazione e Sub.foo() non è un metodo @Oneway, nonostante il fatto che prevale Super.foo() che è.

Se un tipo di annotazione ha la meta-annotazione @Inherited, un'annotazione di quel tipo su una classe farà sì che l'annotazione venga ereditata dalle sottoclassi. Quindi, nell'esempio sopra, se il tipo MyAnnotation aveva l'attributo @Inherited, allora Sub avrebbe l'annotazione MyAnnotation.

Le annotazioni @Inherited non vengono ereditate se utilizzate per annotare qualcosa di diverso da un tipo. Un tipo che implementa una o più interfacce non eredita mai alcuna annotazione dalle interfacce implementate.

+34

Inoltre, trutheality, * io * cercato prima ho chiesto, e ho trovato questa pagina Complimenti, ora fai parte di quei risultati di ricerca. Ecco perché questo sito web è qui. :) Inoltre, la tua risposta è molto più concisa che guardare attraverso quel documento. – Tustin2121

+1

Una domanda per approfondire questo ... se un framework trova il metodo basato sull'annotazione e quindi lo richiama, quale versione del metodo viene invocata? Il metodo della classe figlio dovrebbe sovrascrivere il genitore ma è rispettato con l'invocazione riflessiva? –

9

Hai già trovato la tua risposta: non esiste alcuna disposizione per l'ereditarietà delle annotazioni metodo nel JDK.

Ma scalare la catena di super-classe alla ricerca di metodi annotati è anche facile da implementare:

/** 
* 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

Questo non prevede una situazione in cui l'annotazione richiesta non viene trovata in nessuna delle implementazioni della superclasse - in questo caso getterà 'NoSuchMethodException', anche se' null' sarebbe un valore di ritorno legittimo ... –

+0

@UriAgassi That's by design. Penso che JavaDoc chiarisca. – Saintali

+0

l'unica cosa che javadoc chiarisce è che viene lanciata una 'NoSuchMethodException'. Esso deve generare tale eccezione quando _il metodo non esiste_, non quando _la annotazione non viene trovata _... non è compatibile con l'implementazione originale (che restituirà 'null' in tal caso) –

2

Mentre la risposta alla domanda, come ha chiesto è che Java di Method.getAnnotation() non considera i metodi sostituiti, a volte è utile per trovare queste annotazioni. Ecco una versione più completa della risposta di Saintali che sono attualmente in uso:

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; 
    } 
} 
Problemi correlati