Locke
Locke

Reputation: 8980

Annotation of multiple types of annotation

I want to be able to annotate a class with a list of 2 types of annotations which are related in function, but are completely different in arguments. The order of this list matters though. I have tried looking for this already, but was unable to find any references to this (I was not sure what to call this).

Edit:

What I want to be able to do in the end:

//place holder for example (Abstract)
public @interface A {
}

@Target(PARAMETER)
public @interface B extends A {
    //Gets stuff
    Class type();
    int key();
}

@Target(PARAMETER)
public @interface FlaggedListOfA extends A {
    //List of A
    A[] set();
}

//Goal is to have annotation that does this
@Target(METHOD)
public @interface ImportantFunc {
    A[] dataForA() default {};
    String[] names();
    int property() default 0;
    //etc.
}

//End goal:
public class SomeImportantClass {

    @ImportantFunc(dataForA = {@B(...), @B(...}, ...)
    public void doStuff() {

    }

    //So I can have an end goal of this (Order matters, may contain repeats,
    //and has unknown length!)
    @ImportantFunc(dataForA = {@B(...), @FlaggedListOfA(@B(...), @B(...))}, ...)
    public void doStuffB() {

    }

    @ImportantFunc(dataForA = {@FlaggedListOfA(@B(...)), @FlaggedListOfA(@B(...), @B(...))}, ...)
    public void doStuffC() {

    }

    @ImportantFunc(dataForA = {@FlaggedListOfA(@B(...), @FlaggedListOfA(@B(...), @B(...))), @B(...)}, ...)
    public void doStuffD() {

    }
}

Reflections to get all uses of ImportantFunc (Ex: 100 uses of it) in package and uses this data to choose which function to use. The annotation is to help with the reflection since once it gets the data from @ImportantFunc, it then converts it to input for a library which does the actual choosing of which function to execute (this is internal and can not be modified). This could also be achieved with much longer and more annoying ways, but I was hoping to use annotations to simplify the process of defining all of these functions.

Edit:

Another way this could be solved is finding a way to group two annotations together.

Being able to do this would not be completely ideal, but would definitely make this much more workable:

public @interface Example {
    AnyTypeOfAnnotation[] set();
}

Upvotes: 0

Views: 1630

Answers (2)

Radiodef
Radiodef

Reputation: 37875

One kludgy way to do this is to actually make A be a union of B and C. This means it has all the fields of both B and C, but you only ever use it as either a B or a C.

Here's a working example.

import java.lang.annotation.*;

enum NoType {;}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface A {
    Class<?> data()  default NoType.class; // field from B
    int      dataA() default 0;            // field from C
    String   dataB() default "";           // field from C
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface D {
    A[] value() default {};
}

class Foo {}
class Bar {}

class Example {
    @D({
        @A(data = Bar.class),
        @A(dataA = 5, dataB = "Bla"),
        @A(data = Foo.class)
    })
    public static void main(String[] args) throws Exception {
        for (A a : Example.class.getMethod("main", String[].class)
                        .getAnnotation(D.class).value()) {
            if (a.data() != NoType.class) {
                System.out.printf("B(%s)%n", a.data());
            } else {
                System.out.printf("C(dataA = %d, dataB = \"%s\")%n",
                    a.dataA(), a.dataB());
            }
        }
    }
}

The output of that program is:

B(class Bar)
C(dataA = 5, dataB = "Bla")
B(class Foo)

Of course, it's not a very pretty solution, but it does work.

Upvotes: 2

Jai
Jai

Reputation: 8363

Not sure this would be sufficient for your use case:

public @interface A {
}

public @interface B extends A {
    //Gets stuff
    Class data();
}

public @interface C extends A {
    //Gets different stuff related to same goal
    int dataA();

    String dataB();
}

public @interface D {
    Class<? extends A>[] order();
}


@B(Bar.class)
@C(dataA = 5, dataB = "Bla")
@D(order = {B.class, C.class})
public class SomeImportantClass {

}

This method uses D annotation as a mean to retain annotation order. The bad part is that you cannot add multiple annotations of the same type.

There is another method that makes A, B and C into normal classes.

public abstract class AnnotationAttribute {
    public abstract Class<?>[] getDataTypes();
    public abstract Object[] getData();
}

public class B extends AnnotationAttribute {
    @Override public Class<?>[] getDataTypes() {
        return new Class<?>[] {Foo.class, Bar.class};
    }
    @Override public Object[] getData() {
        return new Object[] {new Foo(), new Bar()};
    }
}

public @interface D {
    Class<? extends AnnotationAttribute>[] data() default {};
}

@D(data = {B.class});
public class Test {
}

This method requires you to create one class for one concrete attribute type. This is because annotations have to be compile-time constant, and referencing via Class requires you to define the class out in code.

Upvotes: 0

Related Questions