簡體   English   中英

如果在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的形式訪問其組件(或鍵/值)。 最重要的部分是應該轉換的此幫助程序:

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之間架起橋梁,這也是一個痛苦。 我認為這與問題無關,因為生成的結構還可以,只有注解放錯了位置。

看起來工作還不錯,除非List或Map中有一個數組。 例如:

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

工作正常(注釋與相應的類型匹配)。 但是當我解析

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

數組和Integer都與@First(“ int”)批注關聯。

我已經調試了代碼,在任何地方都找不到對@First(“ int []”)批注的引用。 我或者缺少一些非常簡單的東西,或者沒有任何意義。 該代碼似乎可以在所有其他情況下使用,甚至是更復雜的情況。 我已經嘗試了好幾個星期,直到最近才找到我認為可行的解決方案。 原來,我之前使用的兩種耦合方法在這種情況下也不起作用(沒有進行此特定測試)。 所以現在我很困。

怎么了?

你說:

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

工作正常(注釋與相應的類型匹配)。

在我看來這是錯誤的。 諸如

@English String @NonEmpty []

讀取為“英語字符串的非空數組”。

現在讓我們考慮您的示例(我已對其進行了略微簡化):

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

您有一個@First(“ list”)數組,其中每個元素都是一個@First(“ array”)列表。 那是你要的嗎? 這不是名稱所隱含的含義。

我不知道在listOfArrayOfInteger情況下您的代碼出了什么問題,但是鑒於您提供的一個示例似乎是不正確的,因此您可能想重新審視“該代碼似乎在所有其他情況下都可以使用”的說法。

在您最初的create調用中,您傳入字段批注,但未傳入AnnotatedType at 因此, create方法負責調用at.getAnnotations() 如果看一下實現,您會發現它只會在數組情況下這樣做。 在所有其他情況下,您的方法將邏輯切換為“傳入的注釋數組是與at關聯的注釋數組”。

問題在於您的第一個示例似乎偶然地起作用。 您沒有顯示@First批注的聲明,但我懷疑@TargetElementType.FIELD ElementType.TYPE_USE都允許使用它。 在這種情況下,聲明形式為

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

是不明確的並且注釋將被記錄為兩個 ,一個字段注釋fieldName和用於注釋List…[]類型。 因此,在第一個示例中無法識別遞歸過程中丟失一個注釋數組的事實,因為它恰好與字段注釋匹配。 但是,一旦TYPE_USE允許注釋,而對FIELD目標不允許,則您的代碼甚至無法與第一個示例一起使用。

因此,您必須確定傳入的批注數組是與at參數關聯的數組還是與周圍上下文關聯的數組。 如果兩者都關聯起來會更容易,因為在這種情況下,您可以通過讓方法檢索該數組而不是調用方來完全擺脫該參數。

您應該記住:

  1. 如果要記錄一個遞歸類型的所有類型注釋字段注釋,則比類型節點多一個注釋數組
  2. 如果要主要處理TYPE_USE批注,則根本不需要處理字段批注

這是一個簡單,直接的解析代碼,用於查找所有注釋:

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

它可以完美地與您的示例字段一起使用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM