views:

503

answers:

2

Do you think you are a java wizard?

That you are well versed in the secrets of the reflection 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);
}

You have to write method solve, so that if it is invoked on method C.x() and Mark.class it returns {c, d}.

(This is not homework, is a real programming assignment for a framework metaprogramming framework I am trying to develop)

+1  A: 

Actually, I don't get how this is even remotely tricky.

Update, forgot to include the contains function and also made a mistake switching Annotation.getClass() with Annotation.annotationType(). This code works

@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

Note that this requires that all the annotations be marked with runtime level retention and also that returning Annotation[] is probably not what you want. You probably want to return a Class[] full of the actual types (in my example, A.class)

Jherico
That actually doesn't work for me. Did you test it?
Yishai
did you specify RetentionPolicy.RUNTIME on your annotations? otherwise - they are erased at runtime.
Andreas Petersson
He's right, my original code didn't work because I was using Annotation.getClass() I should have been using Annotation.annotationType().
Jherico
+5  A: 

This is tested to work. It was indeed harder than it should have been.

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

I guess it just begs the question of why annotationType() works, but getClass() does not.

Yishai
It's easy to get wrapped around the axle when dealing with reflection. annotation.getClass() will return Class<Annotation>, not the class of the annotation it represents. Meta-programming is ugly at times!
rtenhove
My nemesis: 'testing code before releasing it'
Jherico
Specifically, the Annotation object is a proxy object specific to a single class being annotated. Because this is meta, the getClass() is the class of the annotation instance, while annotationType() is the meta equivalent of getClass()
Jherico
Thanks Jherico, that makes sense.
Yishai