Annotation member which holds other annotations?

前端 未结 5 791
耶瑟儿~
耶瑟儿~ 2020-12-29 11:55

I want to create a custom annotation (using Java) which would accept other annotations as parameter, something like:

public @interface ExclusiveOr {
    Anno         


        
相关标签:
5条回答
  • 2020-12-29 12:25

    Depending on the reason why you would want to specify other annotations there are multiple solutions:

    An array of instances of a single annotation type

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

    An array of annotation types instead of instances

    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.

    Use multiple arrays

    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.

    0 讨论(0)
  • 2020-12-29 12:27

    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{
        // ...
    }
    
    0 讨论(0)
  • 2020-12-29 12:36

    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 of Class, 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 any public or protected method declared in class Object or in the interface annotation.Annotation.

    I'm afraid I don't know of a good workaround, but now at least you know why you get the error.

    0 讨论(0)
  • 2020-12-29 12:38

    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.

    0 讨论(0)
  • 2020-12-29 12:42

    You can do:

    Class<? extends Annotation>[] value();
    

    Not sure if that helps, but . . .

    0 讨论(0)
提交回复
热议问题