如果数组在 List 或 Map 中注释,则 Java 8 TYPE_USE 注释不起作用 [英] Java 8 TYPE_USE annotations not behaving if array is annotated inside List or Map

查看:25
本文介绍了如果数组在 List 或 Map 中注释,则 Java 8 TYPE_USE 注释不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一个带有注释的简单验证库,这些注释使用来自 Java 8 的新 TYPE_USE 目标.访问这些东西的方式真的很复杂,给我留下了两个非常混乱的代码,它们做的事情完全一样,但也与他们实际要做的事情密切相关.所以我决定创建一组简单的类来以非常简单和直观的方式保存这些信息.基本上有 TypedClass,它是一个 Class 包装器,但它可以转换为 ListClass(集合或数组)或 MapClass 以作为 TypedClass 访问其组件(或键/值).最重要的部分是这个应该转换的助手:

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 with two very messy codes that do exactly the same thing, but are also very coupled to what they actually were meant to do. So I decided to create a simple set of classes to hold this information in a very easy and intuitive way. Basically there is the TypedClass, that is a Class wrapper, but it can be cast to ListClass (Collection or array) or MapClass to access its components (or key / value ) as TypedClass. The most important piece is this helper that is supposed to convert:

static TypedClass<?> create(Field f) {
    final TypeResolver typeResolver = new TypeResolver();
    ResolvedType type = typeResolver.resolve(f.getGenericType());
    return create(f.getAnnotations(), type, f.getAnnotatedType());
}

private static TypedClass<?> create(Annotation[] annotations, ResolvedType type, AnnotatedType at) {
    if (type.isArray()) {
        AnnotatedType childAnnotatedType = ((AnnotatedArrayType) at).getAnnotatedGenericComponentType();
        ResolvedType childType = type.getArrayElementType();
        return new ListClass<>(type.getErasedType(), annotations, create(at.getAnnotations(), childType, childAnnotatedType));
    } else if (type.isInstanceOf(Collection.class)) {
        AnnotatedType childAnnotatedType = ((AnnotatedParameterizedType) at).getAnnotatedActualTypeArguments()[0];
        ResolvedType childType = type.typeParametersFor(Collection.class).get(0);
        return new ListClass<>(type.getErasedType(), annotations, createForGenerics(childType, childAnnotatedType));
    } else if (type.isInstanceOf(Map.class)) {
        AnnotatedType[] att = ((AnnotatedParameterizedType) at).getAnnotatedActualTypeArguments();
        List<ResolvedType> types = type.typeParametersFor(Map.class);
        TypedClass<?> key = createForGenerics(types.get(0), att[0]);
        TypedClass<?> value = createForGenerics(types.get(1), att[1]);
        return new MapClass<>(type.getErasedType(), annotations, key, value);
    }
    return new TypedClass<>(type.getErasedType(), annotations);
}

private static TypedClass<?> createForGenerics(ResolvedType childType, AnnotatedType childAnnotatedType) {
    return create(childAnnotatedType.getAnnotations(), childType, childAnnotatedType);
}

这里我也使用了com.fasterxml.classmate中的ResolvedType,它只起到了Java中Type和Class之间的桥梁作用,这也是一种痛苦.我认为这与问题无关,因为生成的结构没问题,只是注释放错了位置.

Here I am also using ResolvedType from com.fasterxml.classmate, it only serves to bridge between Type and Class in Java, which is also a pain. I don't think it is relevant for the problem, as the structure generated is ok, only the annotations are misplaced.

它似乎工作得很好,除非 List 或 Map 中有一个数组.例如:

It seems to be working pretty ok, except when there is an array inside a List or Map. For example:

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

工作正常(注释与相应的类型匹配).但是当我解析

Works fine (the annotations are matched with the corresponding types). But when I parse

List<@First("int[]") Integer @First("int") []> listOfArrayOfInteger;

数组和整数都与@First("int") 注释相关联.

Both the array and the Integer get associated with the @First("int") annotation.

我已经调试了我的代码,但在任何地方都找不到对 @First("int[]") 注释的引用.我要么错过了一些非常简单的东西,要么没有任何意义.该代码似乎适用于所有其他场景,即使是更复杂的场景.我已经尝试了几个星期,最近才得到这个我认为它会起作用的解决方案.原来我之前使用的两种耦合方法也不适用于这种情况(没有这个特定的测试).所以现在我很卡.

I have debugged my code and I can't find reference to the @First("int[]") annotation anywhere. I'm either missing something very simple or nothing makes sense. The code seems to work in every other scenarios, even the more complex ones. I've been trying to to this for weeks, just recently got to this solution that I thought it would work. Turns out the two coupled methods I had before don't work for this scenario as well (didn't have this specific test). So now I am very stuck.

怎么了?

推荐答案

在您对 create 的初始调用中,您传入了 field 批注,而不是 field 批注code>AnnotatedType at.因此,方法create 负责调用at.getAnnotations().如果您查看您的实现,您会发现它只会在数组情况下这样做.在所有其他情况下,您的方法将逻辑切换为传入的注释数组是与 at 关联的数组".

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".

问题是你的第一个例子似乎是偶然的.您没有显示 @First 注释的声明,但我怀疑 @TargetElementType.FIELD ElementType.TYPE_USE.在这种情况下,表单的声明

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;

不明确,注解会被记录为bothfieldName的字段注解和List…[]类型的注解.因此,在第一个示例中无法识别在递归期间丢失一个注释数组的事实,因为它恰好与字段注释匹配.但是,一旦仅允许 TYPE_USE 而不允许用于 FIELD 目标的注释,您的代码甚至不适用于您的第一个示例.

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.

因此,您必须决定传入的注释数组是与 at 参数关联的数组还是与周围上下文关联的数组.如果两者都关联会更容易,因为在这种情况下,您可以通过让方法检索该数组而不是调用者来完全摆脱该参数.

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.

你应该记住:

  1. 如果你想记录递归类型的所有类型注解字段注解,你将比类型节点多一个注解数组
  2. 如果你想主要处理TYPE_USE注解,你根本不需要处理字段注解
  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.

这篇关于如果数组在 List 或 Map 中注释,则 Java 8 TYPE_USE 注释不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆