web-dev-qa-db-fra.com

Comment les annotations de méthode Java Java fonctionnent-elles conjointement avec la substitution de méthode?

J'ai une classe parent Parent et une classe enfant Child, définies ainsi:

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

Si j'obtiens une référence Method à Child::foo, childFoo.getAnnotation(MyAnnotation.class) me donnera-t-il @MyAnnotation? Ou sera-ce null?

Je m'intéresse plus généralement à la façon dont l'annotation fonctionne avec l'héritage Java.

62
Travis Webb

Copié textuellement de http://www.Eclipse.org/aspectj/doc/released/adk15notebook/annotations.html#annotation-inheritance :

Héritage d'annotation

Il est important de comprendre les règles relatives à l'héritage des annotations, car celles-ci ont une incidence sur la correspondance des points de jointure basée sur la présence ou l'absence d'annotations.

Par défaut, les annotations ne sont pas héritées. Étant donné le programme suivant

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

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

Alors Sub n'a pas l'annotation MyAnnotation, et Sub.foo() n'est pas une méthode @Oneway, Malgré le fait qu'elle écrase Super.foo() lequel est.

Si un type d'annotation a la méta-annotation @Inherited, Alors une annotation de ce type sur une classe entraînera l'héritage de l'annotation par les sous-classes. Ainsi, dans l'exemple ci-dessus, si le type MyAnnotation avait l'attribut @Inherited, Alors Sub aurait l'annotation MyAnnotation.

Les annotations @Inherited Ne sont pas héritées lorsqu'elles sont utilisées pour annoter autre chose qu'un type. Un type qui implémente une ou plusieurs interfaces n'hérite jamais d'annotations des interfaces qu'il implémente.

72
trutheality

Vous avez déjà trouvé votre réponse: il n'y a aucune disposition pour l'héritage d'annotation de méthode dans le JDK.

Mais gravir la chaîne des super-classes à la recherche de méthodes annotées est également facile à mettre en œuvre:

/**
 * 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);
}
10
Saintali

En utilisant Spring Core, vous pouvez résoudre avec

AnnotationUtils.Java

7
jrey

Bien que la réponse à la question posée soit que la Method.getAnnotation() de Java ne considère pas les méthodes substituées, il est parfois utile de trouver ces annotations. Voici une version plus complète de la réponse de Saintali que j'utilise actuellement:

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;
    }
}
2
Trevor Robinson