Java元编程难题:获取所有被给定注释A注释的注释

4

您是否认为自己是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}。

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


2
你认为我会因为你暗示我不是巫师而在写你的作业时跌倒吗? - ChssPly76
此外,“元数据即谋杀”:http://www.codinghorror.com/blog/archives/001282.html - ChssPly76
2
我是一名Java巫师,这对我来说太琐碎了,所以我只是嘲笑你的巫术。 - Michael Borgwardt
这里唯一令人困惑的是为什么你认为这是一个难题。 - Jherico
你尝试了什么?发生了什么情况?默认的保留策略是什么? - Michael Lloyd Lee mlk
2个回答

6

这已经被测试过可以正常工作。事实上,它比应该的要难一些。

@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>,而不是它所代表的注释类。元编程有时很丑陋! - rtenhove
具体来说,注解对象是一个代理对象,专门用于注释单个类。由于这是元数据,getClass() 是注解实例的类,而 annotationType() 则是 getClass() 的元数据等效项。 - Jherico

1

实际上,我不明白这怎么可能有丝毫的棘手。

更新,忘记包含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?否则,在运行时它们将被擦除。 - Andreas Petersson
他是对的,我的原始代码没有起作用,因为我使用了Annotation.getClass(),而我应该使用Annotation.annotationType()。 - Jherico

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接