如果数组在 List 或 Map 中注释,则 Java 8 TYPE_USE 注释不起作用 [英] Java 8 TYPE_USE annotations not behaving if array is annotated inside List or Map
问题描述
我正在尝试编写一个带有注释的简单验证库,这些注释使用来自 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
注释的声明,但我怀疑 @Target
和 ElementType.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 @Target
s, ElementType.FIELD
and ElementType.TYPE_USE
. In this case, a declaration of the form
@First("array") List… [] fieldName;
不明确,注解会被记录为both,fieldName
的字段注解和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.
你应该记住:
- 如果你想记录递归类型的所有类型注解和字段注解,你将比类型节点多一个注解数组
- 如果你想主要处理
TYPE_USE
注解,你根本不需要处理字段注解
- 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
- 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屋!