2012-04-10 1 views
58

Ich habe eine Elternklasse Parent und ein Kind Klasse Child, so definiert:Wie funktionieren Java-Methodenanmerkungen in Verbindung mit dem Überschreiben von Methoden?

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

Wenn ich einen Method Bezug auf Child::foo erhalten, wird childFoo.getAnnotation(MyAnnotation.class) geben Sie mir @MyAnnotation? Oder wird es null sein?

Ich bin allgemein interessiert, wie oder ob Annotation mit Java-Vererbung funktioniert.

Antwort

60

wörtlich kopiert aus http://www.eclipse.org/aspectj/doc/released/adk15notebook/annotations.html#annotation-inheritance:

Annotationsvererbung

Es ist wichtig, die Regeln in Bezug auf Vererbung von Anmerkungen zu verstehen, da diese einen Einfluss auf den Punkt Matching auf der Anwesenheit Join basiert oder Abwesenheit von Anmerkungen.

Standardmäßig werden Anmerkungen nicht vererbt. das folgende Programm

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

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

Dann Sub hat nicht die MyAnnotation Anmerkung und Sub.foo() ist kein @Oneway Verfahren, trotz der Tatsache, dass es Super.foo() überschreibt, was ist.

Wenn ein Annotationstyp die Meta-Annotation @Inherited hat, bewirkt eine Annotation dieses Typs in einer Klasse, dass die Annotation von Unterklassen geerbt wird. Wenn also im obigen Beispiel der Typ MyAnnotation das Attribut @Inherited hatte, dann hätte Sub die MyAnnotation Annotation.

@Inherited Anmerkungen werden nicht vererbt, wenn sie verwendet werden, um etwas anderes als einen Typ zu kommentieren. Ein Typ, der eine oder mehrere Schnittstellen implementiert, erbt niemals Anmerkungen von den Schnittstellen, die er implementiert.

+34

Auch trutheality, * ich gesucht *, bevor ich fragte, und ich habe diese Seite gefunden. Herzlichen Glückwunsch, Sie sind jetzt Teil dieser Suchergebnisse. Deshalb ist diese Website hier. :) Auch Ihre Antwort ist viel prägnanter als das Durchsehen dieses Dokuments. – Tustin2121

+1

Eine Frage, die das weitergibt ... Wenn ein Framework die Methode findet, die auf der Annotation basiert, und sie dann aufruft, welche Version der Methode wird aufgerufen? Die Methode der Kindklasse sollte die Elternklasse außer Kraft setzen, aber wird diese bei der reflektiven Invokation beachtet? –

9

Sie haben Ihre Antwort bereits gefunden: Es gibt keine Bestimmung für die Vererbung von Methodenannotationen im JDK.

Aber die Super-Klasse-Kette Klettern auf der Suche nach kommentierten Methoden ist auch einfach zu implementieren:

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

Dies ist nicht für eine Situation vorgesehen, in der die angeforderte Annotation in keiner der Superklassen-Implementierungen gefunden wird - in diesem Fall wird "NoSuchMethodException" ausgelöst, obwohl "null" ein zulässiger Rückgabewert wäre ... –

+0

@UriAgassi That's by Design. Ich denke, der JavaDoc macht es klar. – Saintali

+0

Das einzige, was das Javadoc klar macht, ist, dass eine 'NoSuchMethodException' ausgelöst wird. Es _ sollte _ eine solche Ausnahme auslösen, wenn _die Methode nicht existiert_, nicht wenn _die Anmerkung nicht gefunden wird _... es ist nicht kompatibel mit der ursprünglichen Implementierung (die in einem solchen Fall "null" zurückgibt) –

2

Während die Antwort auf die Frage, wie ist darum gebeten, dass die Java Method.getAnnotation() nicht überschriebenen Methoden nicht berücksichtigt, ist es manchmal nützlich, um diese Anmerkungen zu finden. Hier ist eine vollständigere Version von Saintali Antwort, die ich zur Zeit mit:

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