簡體   English   中英

如何使用Java反射來檢查給定的類是實現Iterable <? extends T> ,對於任何給定的T

[英]How to use Java reflection to check a given class is implements Iterable<? extends T>, for any given T

我有一個特定的目標類型(在運行時決定),以及一個可迭代的類,我正在與它進行比較。 我正在嘗試編寫一個檢查類的泛型參數的方法,以查看它是否是可以迭代我的目標類型的子類。 例子:

Class<?> X = SomeObject.class;

matches(X, new ArrayList<SomeObject>()) -> true
matches(X, new ArrayList<SubclassOfSomeObject>()) -> true
matches(X, new ArrayList<SomeOtherObject>()) -> false
matches(X, new ArrayList()) -> true (I think?)
matches(X, new Iterable<SomeObject>() { ... }) -> true
matches(X, new ListOfSomeObjects()) -> true 
                       (where ListOfSomeObjects extends Iterable<SomeObject>)

不幸的是,由於類型擦除和反射API的限制相結合,您正在嘗試做的事情非常復雜。

確實,您可以使用Class.getGenericSuperclassParameterizedType.getActualTypeArguments的組合來獲取超類的泛型ParameterizedType.getActualTypeArguments 這是例如Guava的TypeToken類用於捕獲泛型類型參數的機制。 但是你在這里要求的是接口的泛型類型參數,它可以在繼承鏈的任何一點實現 - 盡管接口本身可以在自由解析或聲明新類型參數的同時繼承。

要演示,請采用以下方法:

static void inspect(Object o) {
    Type type = o.getClass();
    while (type != null) {
        System.out.print(type + " implements");
        Class<?> rawType =
                (type instanceof ParameterizedType)
                ? (Class<?>)((ParameterizedType)type).getRawType()
                : (Class<?>)type;
        Type[] interfaceTypes = rawType.getGenericInterfaces();
        if (interfaceTypes.length > 0) {
            System.out.println(":");
            for (Type interfaceType : interfaceTypes) {
                if (interfaceType instanceof ParameterizedType) {
                    ParameterizedType parameterizedType = (ParameterizedType)interfaceType;
                    System.out.print("  " + parameterizedType.getRawType() + " with type args: ");
                    Type[] actualTypeArgs = parameterizedType.getActualTypeArguments();
                    System.out.println(Arrays.toString(actualTypeArgs));
                }
                else {
                    System.out.println("  " + interfaceType);
                }
            }
        }
        else {
            System.out.println(" nothing");
        }
        type = rawType.getGenericSuperclass();
    }
}

這將反映一個對象並爬上它的繼承鏈來報告它實現的接口及其泛型參數(如果適用)。

讓我們在您列出的第一個案例中嘗試:

inspect(new ArrayList<SomeObject>());

這打印:

class java.util.ArrayList implements:
  interface java.util.List with type args: [E]
  interface java.util.RandomAccess
  interface java.lang.Cloneable
  interface java.io.Serializable
java.util.AbstractList<E> implements:
  interface java.util.List with type args: [E]
java.util.AbstractCollection<E> implements:
  interface java.util.Collection with type args: [E]
class java.lang.Object implements nothing

您可以看到類型參數E尚未解析。 在給定類型擦除的情況下,這是完全可以理解的 - 在運行時,對應於new ArrayList<SomeObject>()的字節碼指令沒有SomeObject概念。

匿名類的情況不同:

inspect(new Iterable<SomeObject>() {
    @Override
    public Iterator<SomeObject> iterator() {
        throw new UnsupportedOperationException();
    }
});

打印:

class sandbox.Main$1 implements:
  interface java.lang.Iterable with type args: [class sandbox.SomeObject]
class java.lang.Object implements nothing

這里,我們在運行時可以使用類型參數,因為匿名類通過實現Iterable<SomeObject>解析類型參數。 ListOfSomeObjects及其任何子類都可以用於相同的原因。

好的,只要繼承鏈中的某個類一路上解析了類型參數E ,我們可以匹配它嗎? 不幸的是,至少沒有上述方法:

inspect(new ArrayList<SomeObject>() { });

這打印:

class sandbox.Main$1 implements nothing
java.util.ArrayList<sandbox.SomeObject> implements:
  interface java.util.List with type args: [E]
  interface java.util.RandomAccess
  interface java.lang.Cloneable
  interface java.io.Serializable
java.util.AbstractList<E> implements:
  interface java.util.List with type args: [E]
java.util.AbstractCollection<E> implements:
  interface java.util.Collection with type args: [E]
class java.lang.Object implements nothing

您可以看到ArrayList的類型參數已知為SomeObject ,但這就是它停止的位置。 類型參數之間沒有連接關系。 原因是這段代碼:

Class<?> rawType =
        (type instanceof ParameterizedType)
        ? (Class<?>)((ParameterizedType)type).getRawType()
        : (Class<?>)type;
Type[] interfaceTypes = rawType.getGenericInterfaces();

getGenericInterfaces是獲取接口的類型參數信息的唯一方法,但該方法由Class聲明,而不是Type 每當方法有一個ParameterizedType實例,它保存表示其子類的泛型的狀態時,就強制調用getRawType ,它返回沒有類型參數信息的Class單例。 它是一個catch-22導致只能獲得使用具體類型參數實現的接口的類型參數。

我不知道任何反射API方法將類型參數與它們解析的參數匹配。 從理論上講,人們可以編寫反映代碼,爬上繼承鏈,找到實現Iterable (或子接口)的類,然后向下爬,直到匹配相應的類型參數。 不幸的是,我想不出這是如何實現的。 類可以自由地聲明具有任何名稱的類型參數以及他們想要的任何順序,因此基於名稱或位置的天真匹配是不對的。 也許其他人可以提供解決方案。

我不認為你可以從一個實例獲得泛型參數類型:

new ArrayList<Integer>();

如果你有一個持有實例的字段,你可以從那里得到它:

private List<Integer> list = ArrayList<Integer>();

看到這個類似的問題: 獲取java.util.List的泛型類型

暫無
暫無

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

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