I want to create a custom annotation (using Java) which would accept other annotations as parameter, something like:
public @interface ExclusiveOr {
Anno
Depending on the reason why you would want to specify other annotations there are multiple solutions:
Probably not what you meant in your question, but if you want to specify multiple instances of a single annotation type it's certainly possible:
public @interface Test {
SomeAnnotation[] value();
}
If you do not need to specify any parameters on the individual annotations you can just user their class objects instead of instances.
public @interface Test {
Class<? extends Annotation>[] value();
}
But an enum would of course also do the trick in most situations.
If the set of possible annotation types you want to use is limited, you can create a separate parameter for each one.
public @interface Test {
SomeAnnotation[] somes() default { };
ThisAnnotation[] thiss() default { };
ThatAnnotation[] thats() default { };
}
Giving a default value to each member makes it possible to only specify arrays for the types you need.
I myself hereby propose a workaround for the given problem:
Well, what I wanted to make possible was something like that:
@Contract({
@ExclusiveOr({
@IsType(IAtomicType.class),
@Or({
@IsType(IListType.class),
@IsType(ISetType.class)
})
})
})
Proposed workaround:
Define a class with parameter-less constructor (which will be called by your own annotation processor later) in following way:
final class MyContract extends Contract{
// parameter-less ctor will be handeled by annotation processor
public MyContract(){
super(
new ExclusiveOr(
new IsType(IAtomicType.class),
new Or(
new IsType(IListType.class),
new IsType(ISetType.class)
)
)
);
}
}
usage:
@Contract(MyContract.class)
class MyClass{
// ...
}
The error is produced because you can't use interfaces as annotation values (change it to Comparable
and you'll get the same error). From the JLS:
It is a compile-time error if the return type of a method declared in an annotation type is any type other than one of the following: one of the primitive types,
String
,Class
and any invocation ofClass
, an enum type, an annotation type, or an array of one of the preceding types. It is also a compile-time error if any method declared in an annotation type has a signature that is override-equivalent to that of anypublic
orprotected
method declared in classObject
or in the interfaceannotation.Annotation
.
I'm afraid I don't know of a good workaround, but now at least you know why you get the error.
I just ran into this exact problem, but (inspired by @ivan_ivanovich_ivanoff) I have discovered a way to specify a bundle of any combination of Annotations as an annotation member: use a prototype / template class.
In this example I define a WhereOr (i.e. a "where clause" for my model annotation) which I need to contain arbitrary Spring meta-annotations (like @Qualifier meta-annotations).
The minor (?) defect in this is the forced dereferencing that separates the implementation of the where clause with the concrete type that it describes.
@Target({})
@Retention(RetentionPolicy.RUNTIME)
public @interface WhereOr {
Class<?>[] value() default {};
}
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface JsonModel {
Class<?> value();
WhereOr where() default @WhereOr;
}
public class Prototypes {
@Qualifier("myContext")
@PreAuthorize("hasRole('ROLE_ADMINISTRATOR')")
public static class ExampleAnd {
}
}
@JsonModel(
value = MusicLibrary.class,
where = @WhereOr(Prototypes.ExampleAnd.class)
)
public interface JsonMusicLibrary {
@JsonIgnore
int getMajorVersion();
// ...
}
I will programmatically extract the possible valid configurations from the "where clause" annotation. In this case I also use the prototypes class as a logical AND grouping and the array of classes as the logical OR.
You can do:
Class<? extends Annotation>[] value();
Not sure if that helps, but . . .