【问题标题】:How to get annotation from overridden method in Java?如何从Java中的重写方法获取注释?
【发布时间】:2018-03-05 07:22:37
【问题描述】:

我有以下情况

class Parent {
    @SomeAnnotation(someValue)
    public void someMethod(){...}
}

class Child extends Parent {
    @Override
    public void someMethod(){...}
}

当我引用方法 Child.someMethod 时,我需要获取 @SomeAnnotation

使用Child.getSuperclass() 我可以得到Parent.class。此外,我找到了一个解决方案here 来引用Parent.someMethodMethodHandle,所以我有

MethodHandle parentMethodHandle = MethodHandles.lookup().findSpecial(
        Parent.class, childSomeMethod.getName(),
        MethodType.methodType(Void.class), Child.class);

但是,我找不到从 parentMethodHandlegetAnnotation() 的方法 Parent.someMethod 的引用。请帮忙。

【问题讨论】:

  • @SomeAnnotation 是你的注释类吗?
  • @daniu 是的,这是我的注释。但这有关系吗?

标签: java reflection annotations


【解决方案1】:

这是一个完整的解决方案。我相信这对于 所有 情况应该是正确的,除非在 class A extends B 这样的奇怪情况下,AB 由不同的类加载器加载。这也可能不适用于其他 JVM 语言(例如 Scala),其中方法可以具有相同的擦除但不能相互覆盖(例如 void m(List<A> l)void m(List<B> l),这会导致 Java 中的编译错误)。

事实证明,正确执行此操作有点复杂,因为您必须确保两个方法实际上相互覆盖,而目前 Java SE 中还没有方法可以做到这一点。无论如何,在网站上都有有用的代码。

package mcve.reflect;

import java.util.*;
import java.lang.reflect.*;
import java.lang.annotation.*;

public final class MCVEReflect {
    private MCVEReflect() {}

    /**
     * Returns the 0th element of the list returned by
     * {@code getAnnotations}, or {@code null} if the
     * list would be empty.
     * 
     * @param  <A> the type of the annotation to find.
     * @param  m   the method to begin the search from.
     * @param  t   the type of the annotation to find.
     * @return the first annotation found of the specified type which
     *         is present on {@code m}, or present on any methods which
     *         {@code m} overrides.
     * @throws NullPointerException if any argument is {@code null}.
     * @see    MCVEReflect#getAnnotations(Method, Class)
     */
    public static <A extends Annotation> A getAnnotation(Method m, Class<A> t) {
        List<A> list = getAnnotations(m, t);
        return list.isEmpty() ? null : list.get(0);
    }

    /**
     * Let {@code D} be the class or interface which declares the method
     * {@code m}.
     * <p>
     * Returns a list of all of the annotations of the specified type
     * which are either present on {@code m}, or present on any methods
     * declared by a supertype of {@code D} which {@code m} overrides.
     * <p>
     * Annotations are listed in order of nearest proximity to {@code D},
     * that is, assuming {@code D extends E} and {@code E extends F}, then
     * the returned list would contain annotations in the order of
     * {@code [D, E, F]}. A bit more formally, if {@code Sn} is the nth
     * superclass of {@code D} (where {@code n} is an integer starting at 0),
     * then the index of the annotation present on {@code Sn.m} is {@code n+1},
     * assuming annotations are present on {@code m} for every class.
     * <p>
     * Annotations from methods declared by the superinterfaces of {@code D}
     * appear <em>last</em> in the list, in order of their declaration,
     * recursively. For example, if {@code class D implements X, Y} and
     * {@code interface X extends Z}, then annotations will appear in the
     * list in the order of {@code [D, X, Z, Y]}.
     * 
     * @param  <A> the type of the annotation to find.
     * @param  m   the method to begin the search from.
     * @param  t   the type of the annotation to find.
     * @return a list of all of the annotations of the specified type
     *         which are either present on {@code m}, or present on any
     *         methods which {@code m} overrides.
     * @throws NullPointerException if any argument is {@code null}.
     */
    public static <A extends Annotation> List<A> getAnnotations(Method m, Class<A> t) {
        List<A> list = new ArrayList<>();
        Collections.addAll(list, m.getAnnotationsByType(t));
        Class<?> decl = m.getDeclaringClass();

        for (Class<?> supr = decl; (supr = supr.getSuperclass()) != null;) {
            addAnnotations(list, m, t, supr);
        }
        for (Class<?> face : getAllInterfaces(decl)) {
            addAnnotations(list, m, t, face);
        }

        return list;
    }

    private static Set<Class<?>> getAllInterfaces(Class<?> c) {
        Set<Class<?>> set = new LinkedHashSet<>();
        do {
            addAllInterfaces(set, c);
        } while ((c = c.getSuperclass()) != null);
        return set;
    }
    private static void addAllInterfaces(Set<Class<?>> set, Class<?> c) {
        for (Class<?> i : c.getInterfaces()) {
            if (set.add(i)) {
                addAllInterfaces(set, i);
            }
        }
    }
    private static <A extends Annotation> void addAnnotations
            (List<A> list, Method m, Class<A> t, Class<?> decl) {
        try {
            Method n = decl.getDeclaredMethod(m.getName(), m.getParameterTypes());
            if (overrides(m, n)) {
                Collections.addAll(list, n.getAnnotationsByType(t));
            }
        } catch (NoSuchMethodException x) {
        }
    }

    /**
     * @param  a the method which may override {@code b}.
     * @param  b the method which may be overridden by {@code a}.
     * @return {@code true} if {@code a} probably overrides {@code b}
     *         and {@code false} otherwise.
     * @throws NullPointerException if any argument is {@code null}.
     */
    public static boolean overrides(Method a, Method b) {
        if (!a.getName().equals(b.getName()))
            return false;
        Class<?> classA = a.getDeclaringClass();
        Class<?> classB = b.getDeclaringClass();
        if (classA.equals(classB))
            return false;
        if (!classB.isAssignableFrom(classA))
            return false;
        int modsA = a.getModifiers();
        int modsB = b.getModifiers();
        if (Modifier.isPrivate(modsA) || Modifier.isPrivate(modsB))
            return false;
        if (Modifier.isStatic(modsA) || Modifier.isStatic(modsB))
            return false;
        if (Modifier.isFinal(modsB))
            return false;
        if (compareAccess(modsA, modsB) < 0)
            return false;
        if ((isPackageAccess(modsA) || isPackageAccess(modsB))
            && !Objects.equals(classA.getPackage(), classB.getPackage()))
            return false;
        if (!b.getReturnType().isAssignableFrom(a.getReturnType()))
            return false;
        Class<?>[] paramsA = a.getParameterTypes();
        Class<?>[] paramsB = b.getParameterTypes();
        if (paramsA.length != paramsB.length)
            return false;
        for (int i = 0; i < paramsA.length; ++i)
            if (!paramsA[i].equals(paramsB[i]))
                return false;
        return true;
    }

    public static boolean isPackageAccess(int mods) {
        return (mods & ACCESS_MODIFIERS) == 0;
    }

    private static final int ACCESS_MODIFIERS =
        Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE;
    private static final List<Integer> ACCESS_ORDER =
        Arrays.asList(Modifier.PRIVATE,
                      0,
                      Modifier.PROTECTED,
                      Modifier.PUBLIC);
    public static int compareAccess(int lhs, int rhs) {
        return Integer.compare(ACCESS_ORDER.indexOf(lhs & ACCESS_MODIFIERS),
                               ACCESS_ORDER.indexOf(rhs & ACCESS_MODIFIERS));
    }
}

这也是一个 JUnit 4 测试:

package mcve.reflect;

import static mcve.reflect.MCVEReflect.*;

import java.lang.reflect.*;
import java.lang.annotation.*;
import java.util.*;
import static java.util.stream.Collectors.*;
import org.junit.*;
import static org.junit.Assert.*;

public class MCVEReflectTest {
    public MCVEReflectTest() {
    }

    @Retention(RetentionPolicy.RUNTIME)
    @interface AnnoContainer {
        Anno[] value();
    }

    @Repeatable(AnnoContainer.class)
    @Retention(RetentionPolicy.RUNTIME)
    @interface Anno {
        String value();
    }

    interface I {
        @Anno("I.m")
        void m();
    }
    interface J extends I {
        @Anno("J.m")
        @Override
        void m();
    }
    interface K {
        @Anno("K.m")
        void m();
    }

    @Test
    public void getAnnotationsTest() throws NoSuchMethodException {
        try {
            getAnnotations(null, Anno.class);
            fail();
        } catch (NullPointerException x) {
        }
        try {
            getAnnotations(List.class.getMethod("size"), null);
            fail();
        } catch (NullPointerException x) {
        }

        class A {
            @Anno("A.m")
            void m() {}
            @Anno("A.m(int)")
            void m(int a) {}
        }
        class B extends A implements K {
            @Anno("B.m 1")
            @Anno("B.m 2")
            @Override
            public void m() {}
        }
        class C extends B implements J {
            @Anno("C.m")
            @Override
            public void m() {}
        }

        System.out.println(Arrays.toString(B.class.getInterfaces()));

        List<Anno>   annos;
        List<String> expect;
        List<String> actual;

        annos  = getAnnotations(A.class.getDeclaredMethod("m"), Anno.class);
        expect = Arrays.asList("A.m");
        actual = annos.stream().map(Anno::value).collect(toList());

        assertEquals(expect, actual);

        annos  = getAnnotations(B.class.getDeclaredMethod("m"), Anno.class);
        expect = Arrays.asList("B.m 1", "B.m 2", "A.m", "K.m");
        actual = annos.stream().map(Anno::value).collect(toList());

        assertEquals(expect, actual);

        annos  = getAnnotations(C.class.getDeclaredMethod("m"), Anno.class);
        expect = Arrays.asList("C.m", "B.m 1", "B.m 2", "A.m", "J.m", "I.m", "K.m");
        actual = annos.stream().map(Anno::value).collect(toList());

        assertEquals(expect, actual);

        annos  = getAnnotations(J.class.getDeclaredMethod("m"), Anno.class);
        expect = Arrays.asList("J.m", "I.m");
        actual = annos.stream().map(Anno::value).collect(toList());

        assertEquals(expect, actual);

        annos = getAnnotations(Object.class.getMethod("toString"), Anno.class);
        assertEquals(Collections.emptyList(), annos);
    }

    private boolean overrides(Method a, Method b) {
        return MCVEReflect.overrides(a, b);
    }

    private boolean overrides(Class<?> classA, String nameA,
                              Class<?> classB, String nameB)
            throws NoSuchMethodException {
        return MCVEReflect.overrides(classA.getDeclaredMethod(nameA),
                                     classB.getDeclaredMethod(nameB));
    }

    private boolean overrides(Class<?> classA, Class<?> classB, String name)
            throws NoSuchMethodException {
        return overrides(classA, name, classB, name);
    }

    @Test
    public void overridesTest() throws NoSuchMethodException {
        try {
            overrides(null, List.class.getMethod("size"));
            fail();
        } catch (NullPointerException x) {
        }
        try {
            overrides(List.class.getMethod("size"), null);
            fail();
        } catch (NullPointerException x) {
        }

        assertTrue("this is an override", overrides(ArrayList.class, "size",  List.class, "size"));
        assertFalse("same method",        overrides(List.class,      "size",  List.class, "size"));
        assertFalse("different methods",  overrides(ArrayList.class, "clear", List.class, "size"));

        class A { private void m() {} }
        class B extends A { private void m() {} }

        assertFalse("private methods", overrides(B.class, "m", A.class, "m"));

        class C { public void m() {} }
        class D { public void m() {} }

        assertFalse("no inheritance", overrides(D.class, "m", C.class, "m"));

        class E { public void m() {} }
        class F { public void m() {} }
        class G extends F { @Override public void m() {} }

        assertTrue("yes inheritance", overrides(G.class, "m", F.class, "m"));
        assertFalse("no inheritance", overrides(G.class, "m", E.class, "m"));

        class H {
            public void m(char a) {}
            public void m(long a) {}
        }
        class I extends H {
            @Override public void m(char a) {}
            @Override public void m(long a) {}
        }

        assertTrue("same parameters",
                   overrides(I.class.getDeclaredMethod("m", char.class),
                             H.class.getDeclaredMethod("m", char.class)));
        assertFalse("different parameters",
                    overrides(I.class.getDeclaredMethod("m", char.class),
                              H.class.getDeclaredMethod("m", long.class)));

        class SubHashMap extends HashMap<Object, Object> {
            void reinitialize() {
            }
        }

        String reinitialize = "reinitialize";
        // if this throws, find another method as a replacement
        Method m = HashMap.class.getDeclaredMethod(reinitialize);
        assertTrue(isPackageAccess(m.getModifiers()));
        assertFalse(Modifier.isFinal(m.getModifiers()));

        assertFalse("would override, except they are in different packages",
                    overrides(SubHashMap.class, HashMap.class, reinitialize));
    }

    @Test
    public void compareAccessTest() {
        assertEquals( 0, compareAccess(Modifier.PUBLIC,    Modifier.PUBLIC));
        assertEquals(+1, compareAccess(Modifier.PUBLIC,    Modifier.PROTECTED));
        assertEquals(+1, compareAccess(Modifier.PUBLIC,    0));
        assertEquals(+1, compareAccess(Modifier.PUBLIC,    Modifier.PRIVATE));
        assertEquals(-1, compareAccess(Modifier.PROTECTED, Modifier.PUBLIC));
        assertEquals( 0, compareAccess(Modifier.PROTECTED, Modifier.PROTECTED));
        assertEquals(+1, compareAccess(Modifier.PROTECTED, 0));
        assertEquals(+1, compareAccess(Modifier.PROTECTED, Modifier.PRIVATE));
        assertEquals(-1, compareAccess(0,                  Modifier.PUBLIC));
        assertEquals(-1, compareAccess(0,                  Modifier.PROTECTED));
        assertEquals( 0, compareAccess(0,                  0));
        assertEquals(+1, compareAccess(0,                  Modifier.PRIVATE));
        assertEquals(-1, compareAccess(Modifier.PRIVATE,   Modifier.PUBLIC));
        assertEquals(-1, compareAccess(Modifier.PRIVATE,   Modifier.PROTECTED));
        assertEquals(-1, compareAccess(Modifier.PRIVATE,   0));
        assertEquals( 0, compareAccess(Modifier.PRIVATE,   Modifier.PRIVATE));
        int notModsL = Modifier.PRIVATE | Modifier.PUBLIC;
        int notModsR = Modifier.PRIVATE | Modifier.PROTECTED;
        assertEquals("this might as well be undefined, but it's here for posterity",
                     0, compareAccess(notModsL, notModsR));
    }
}

【讨论】:

    【解决方案2】:

    我认为您已经获得了超类方法并测试了注释。除此之外java不支持获取超级方法注解。

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
    @Inherited
    public @interface SomeAnnotation {
    }
    
    @SomeAnnotation
    public class Parent {
    
        @SomeAnnotation
        public void someMethod() {
            System.out.println("Parent");
        }
    }
    
    public class Child extends Parent {
    
        @Override
        public void someMethod() {
            super.someMethod();
        }
    
        public static void main(String[] args) throws NoSuchMethodException {
            Class clazz = Child.class;
    
            System.out.println("Child Method");
            Method method = clazz.getMethod("someMethod");
            Arrays.stream(method.getDeclaredAnnotations()).forEach(annotation -> System.out.println(annotation));
    
            System.out.println("Parent Method");
            Method superMethod = clazz.getSuperclass().getMethod("someMethod");
            Arrays.stream(superMethod.getDeclaredAnnotations()).forEach(annotation -> System.out.println(annotation));
    
            System.out.println("Class Level support inherited annotations. ");
            Arrays.stream(clazz.getAnnotations()).forEach(annotation -> System.out.println(annotation));
        }
    }
    

    您还可以做一些通用的方法是动态访问超类。作为

    public void printAnnotationInMethod(String methodName, Class clazz) {
            try {
                Method method = clazz.getDeclaredMethod(methodName);
                Arrays.stream(method.getDeclaredAnnotations()).forEach(annotation -> System.out.println(annotation));
                if (null != clazz.getSuperclass()) {
                    printAnnotationInMethod(methodName, clazz.getSuperclass());
                }
            } catch (NoSuchMethodException e) {
                //e.printStackTrace();
            } catch (SecurityException e) {
                //e.printStackTrace();
            }
        }
    

    @Inherited 在注解中仅支持使用 TYPE 的注解。 (仅限班级)

    供参考 Java Inheritance annotation

    【讨论】:

      【解决方案3】:

      您需要将@SomeAnnotation 声明为@Inherited

      @Inherited
      @interface SomeAnnotation {}
      

      【讨论】:

      【解决方案4】:

      这是你想要的,还是我错过了什么?

          @Retention(RetentionPolicy.RUNTIME)
          @interface SomeAnnotation{
          }
      
          class Parent {
              @SomeAnnotation()
              public void someMethod(){
      
              }
          }
      
          class Child extends Parent {
              @Override
              public void someMethod(){
              }
          }
      
          class Test{
              public static void main(String[] args) throws NoSuchMethodException {
                  Child child = new Child();
                  Method someMethodChild = child.getClass().getMethod("someMethod");
                  Class<?> superclass = someMethodChild.getDeclaringClass().getSuperclass();
                  System.out.println(superclass.getMethod("someMethod").getAnnotations().length); //prints 1
              }
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-02-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多