【问题标题】:Java metaprogramming conundrum: get all annotations that are themselves annotated by a given annotation AJava元编程难题:获取本身由给定注释A注释的所有注释
【发布时间】:2010-11-24 11:20:13
【问题描述】:

你认为你是一个 java 向导吗?

您是否精通反射 API 的秘密?

public @interface @a {}
public @interface @b {}
@Mark public @interface @c {}    
@Mark public @interface @d {}
public @interface @e {}

public Class C
{
    @a @b @c @d @e public void x();
}

public class Solver
{
    public Annotation[] solve(Method m, Class c);
}

您必须编写方法solve,这样如果在方法C.x() 和Mark.class 上调用它,它就会返回{c, d}。

(这不是家庭作业,是我正在尝试开发的框架元编程框架的真正编程作业)

【问题讨论】:

  • 你认为我会因为你暗示我是(不是)巫师而为你的作业编写代码而绊倒吗?
  • 另外,Meta 是谋杀:codinghorror.com/blog/archives/001282.html
  • 我是一个java向导,这对我来说太琐碎了,所以我只是对你嗤之以鼻。
  • 这里唯一令人费解的是为什么你认为这是一个难题。
  • 你尝试了什么?发生了什么?什么是默认保留政策?

标签: java metaprogramming puzzle annotations


【解决方案1】:

这是经过测试的。这确实比原本应该的要难。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface a{}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface b{}

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
public @interface Mark{}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Mark
public @interface c{}

public static class D {
    @a @b @c
    public void x() {}
}

public static void main(String[] args) throws Exception {
    Method m = D.class.getMethod("x");

    Collection<Annotation> ret = new HashSet<Annotation>();
    Annotation[] annotations = m.getAnnotations();
    for (Annotation annotation : annotations) {
        Annotation subAnnots = annotation.annotationType().getAnnotation(Mark.class);
        if (subAnnots != null) {
            ret.add(annotation);
        }
    }
    System.out.println(ret);
}

我猜这只是引出了annotationType() 为何有效,而getClass() 无效的问题。

【讨论】:

  • 在处理反射时很容易缠绕在轴上。 annotation.getClass() 将返回 Class,而不是它所代表的注释的类。元编程有时很丑陋!
  • 具体来说,Annotation 对象是特定于被注释的单个类的代理对象。因为这是meta,所以getClass()是注解实例的类,而annotationType()是getClass()的meta等价物
  • 感谢 Jherico,这很有道理。
【解决方案2】:

实际上,我不明白这有多么棘手。

更新,忘记包含 contains 函数,并且在将 Annotation.getClass() 与 Annotation.annotationType() 切换时也犯了错误。此代码有效

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Mark
public @interface A {}

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface B {}

@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.TYPE)
public @interface Mark {}

public class C {
@A @B public void f() {}
}

public class Solver {
public static boolean  contains(Annotation a, Class<?> targetAnnotation) {
    Class<?> c = a.annotationType();
    Annotation[] cas = c.getAnnotations();
    for (Annotation aa : cas) {
        if (aa.annotationType().equals(targetAnnotation)) {
            return true;
        }
    }
    return false;
}

public static Annotation[] getMarked(Method m) {
    List<Annotation> retVal = new ArrayList<Annotation>();
    for (Annotation a : m.getAnnotations()) {
        if (contains(a.getClass().getAnnotations(), Mark.class) {
            retVal.add(a);
        }
    }
    return retVal.toArray(new Annotation[]{});
}

public static void main(String[] args) throws SecurityException, NoSuchMethodException {
    Annotation[] result = getMarked(C.class.getMethod("f"));    
}
} // solver

请注意,这要求所有注释都标记为运行时级别保留,并且返回 Annotation[] 可能不是您想要的。您可能希望返回一个包含实际类型的 Class[](在我的示例中为 A.class)

【讨论】:

  • 您是否在注释中指定了 RetentionPolicy.RUNTIME?否则 - 它们在运行时被删除。
  • 他是对的,我的原始代码不起作用,因为我使用的是 Annotation.getClass() 我应该一直使用 Annotation.annotationType()。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-27
  • 2011-12-07
  • 1970-01-01
  • 1970-01-01
  • 2013-03-11
  • 2015-09-19
相关资源
最近更新 更多