Java 8 TYPE_USE annotations not behaving if array is annotated inside List or Map

后端 未结 2 1179
失恋的感觉
失恋的感觉 2021-01-16 05:16

I\'m trying to write a simple validation lib with annotations that use the new TYPE_USE target from Java 8. The way to access these things is really complex, and left me wit

相关标签:
2条回答
  • 2021-01-16 06:01

    In your initial call to create you pass in the field annotations but not the annotations for the AnnotatedType at. Therefore, the method create is responsible for calling at.getAnnotations(). If you look at your implementation you will find out that it will do so only in the array case. In all other cases, your method switches the logic to “the passed in annotation array is the one associated with at”.

    The problem is that your first example seems to work by accident. You didn’t show the declaration of your @First annotation but I suspect that it is allowed for both @Targets, ElementType.FIELD and ElementType.TYPE_USE. In this case, a declaration of the form

    @First("array") List… [] fieldName;
    

    is ambiguous and the annotation will be recorded as both, a field annotation for fieldName and an annotation for the List…[] type. So the fact that one annotation array is lost during your recursion is not recognized in the first example because it happens to match the field annotations. But once an annotation is allowed for TYPE_USE only but not FIELD targets, your code doesn’t even work with your first example.

    So you have to decide whether the passed in annotation array shall be the one associated with the at parameter or of the surrounding context. It would be easier if both are associated, as in this case you could get rid of that parameter entirely by letting the method retrieve that array rather than the caller.

    You should keep in mind:

    1. If you want to record all type annotations of a recursive type and the field annotations, you will have one more annotation array than type nodes
    2. If you want to process TYPE_USE annotation primarily, you don’t need to deal with field annotations at all

    Here is a simple, straight-forward parsing code finding all annotations:

    public static void fullType(Field f) {
        AnnotatedType at = f.getAnnotatedType();
        fullType("\t", at);
        System.out.println(f.getName());
    }
    public static void fullType(String header, AnnotatedType at) {
        final boolean arrayType = at instanceof AnnotatedArrayType;
        if(arrayType) {
            fullType(header+"\t",
                ((AnnotatedArrayType)at).getAnnotatedGenericComponentType());
        }
        for(Annotation a: at.getAnnotations())
            System.out.println(header+a);
        if(arrayType) {
            System.out.println(header+"[]");
        }
        else if(at instanceof AnnotatedParameterizedType) {
            AnnotatedParameterizedType apt = (AnnotatedParameterizedType)at;
            System.out.println(header
                +((ParameterizedType)apt.getType()).getRawType().getTypeName());
            System.out.println(header+'<');
            String subHeader=header+"\t";
            for(AnnotatedType typeArg:
                apt.getAnnotatedActualTypeArguments())
                fullType(subHeader, typeArg);
            System.out.println(header+'>');
        }
        else if(at instanceof AnnotatedTypeVariable) {
            // when appearing in a Field’s type, it refers to Class’ type variables
            System.out.println(header+at.getType().getTypeName());
        }
        else if(at instanceof AnnotatedWildcardType) {
            System.out.println(header+"?");
            final AnnotatedWildcardType awt = (AnnotatedWildcardType)at;
            AnnotatedType[] bounds=awt.getAnnotatedLowerBounds();
            if(bounds==null || bounds.length==0) {
                bounds=awt.getAnnotatedUpperBounds();
                if(bounds==null || bounds.length==0) return;
                System.out.println(header+"extends");
            }
            else System.out.println(header+"super");
            header+="\t";
            for(AnnotatedType b: bounds) fullType(header, b);
        }
        else {
            assert at.getType().getClass()==Class.class;
            System.out.println(header+at.getType().getTypeName());
        }
    }
    

    It works flawlessly with your example fields.

    0 讨论(0)
  • 2021-01-16 06:05

    You said:

    @First("array") List<@First("string") String> @First("list") [] arrayOfListOfString;
    

    Works fine (the annotations are matched with the corresponding types).

    This looks wrong to me. A type such as

    @English String @NonEmpty []
    

    is read as "non-empty array of English strings".

    Now let's consider your example (which I have simplified slightly):

    @First("array") List @First("list") [] arrayOfList;
    

    You have a @First("list") array, where each element is a @First("array") List. Is that what you want? It's not what is implied by the names.

    I don't know what is wrong with your code in the listOfArrayOfInteger case, but you might want to revisit your claim "The code seems to work in every other scenario" given that the one example that you gave seems to be incorrect.

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