简体   繁体   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

I have a specific target type (decided at runtime), and an iterable class that I'm comparing to it. 我有一个特定的目标类型(在运行时决定),以及一个可迭代的类,我正在与它进行比较。 I'm trying to write a method that checks the generic parameters of the class, to see if it's an iterable of something that subclasses my target type. 我正在尝试编写一个检查类的泛型参数的方法,以查看它是否是可以迭代我的目标类型的子类。 Examples: 例子:

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>)

Unfortunately, what you're trying to do is very involved, because of the combination of type erasure and limitations of the reflections API. 不幸的是,由于类型擦除和反射API的限制相结合,您正在尝试做的事情非常复杂。

It's true that you can get the generic arguments of a superclass using a combination of Class.getGenericSuperclass and ParameterizedType.getActualTypeArguments . 确实,您可以使用Class.getGenericSuperclassParameterizedType.getActualTypeArguments的组合来获取超类的泛型ParameterizedType.getActualTypeArguments This is the mechanism that eg Guava's TypeToken class uses to capture generic type arguments. 这是例如Guava的TypeToken类用于捕获泛型类型参数的机制。 But what you're asking for here is the generic type argument of an interface that may have been implemented at any point in the inheritance chain - notwithstanding that interfaces can themselves inherit from each other while freely resolving or declaring new type parameters. 但是你在这里要求的是接口的泛型类型参数,它可以在继承链的任何一点实现 - 尽管接口本身可以在自由解析或声明新类型参数的同时继承。

To demonstrate, take the following method: 要演示,请采用以下方法:

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

This will reflect on an object and climb its inheritance chain to report on its implemented interfaces and their generic arguments (if applicable). 这将反映一个对象并爬上它的继承链来报告它实现的接口及其泛型参数(如果适用)。

Let's try it on the first case you listed: 让我们在您列出的第一个案例中尝试:

inspect(new ArrayList<SomeObject>());

This prints: 这打印:

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

You can see that the type parameter E has not been resolved. 您可以看到类型参数E尚未解析。 This is perfectly understandable given type erasure - at runtime the bytecode instructions corresponding to new ArrayList<SomeObject>() have no concept of SomeObject . 在给定类型擦除的情况下,这是完全可以理解的 - 在运行时,对应于new ArrayList<SomeObject>()的字节码指令没有SomeObject概念。

The case of the anonymous class is different: 匿名类的情况不同:

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

Prints: 打印:

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

Here, we have the type argument available at runtime since the anonymous class resolved the type parameter by implementing Iterable<SomeObject> . 这里,我们在运行时可以使用类型参数,因为匿名类通过实现Iterable<SomeObject>解析类型参数。 ListOfSomeObjects and any of its subclasses would work for the same reason. ListOfSomeObjects及其任何子类都可以用于相同的原因。

Okay, so as long as some class in the inheritance chain resolves the type parameter E along the way, we can match against it? 好的,只要继承链中的某个类一路上解析了类型参数E ,我们可以匹配它吗? Unfortunately no, at least not with the method above: 不幸的是,至少没有上述方法:

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

This prints: 这打印:

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

You can see that the type argument for ArrayList is known to be SomeObject , but that's where it stops. 您可以看到ArrayList的类型参数已知为SomeObject ,但这就是它停止的位置。 There's no connecting relationship between the type parameters. 类型参数之间没有连接关系。 The reason is this bit of code: 原因是这段代码:

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

getGenericInterfaces is the only way to get type argument information for the interfaces, but that method is declared by Class , not Type . getGenericInterfaces是获取接口的类型参数信息的唯一方法,但该方法由Class声明,而不是Type Whenever the method has a ParameterizedType instance, which holds the state representing its subclass's generic-ness, it's forced to call getRawType , which returns the Class singleton devoid of type argument information. 每当方法有一个ParameterizedType实例,它保存表示其子类的泛型的状态时,就强制调用getRawType ,它返回没有类型参数信息的Class单例。 It's a catch-22 that results in only being able to get the type arguments of interfaces implemented with concrete type arguments. 它是一个catch-22导致只能获得使用具体类型参数实现的接口的类型参数。

I don't know of any reflection API method that matches type arguments with the parameters they resolve. 我不知道任何反射API方法将类型参数与它们解析的参数匹配。 Theoretically one could write reflective code that climbed up the inheritance chain, located the class that implemented Iterable (or a subinterface) and then climbed back down until it matched the corresponding type argument. 从理论上讲,人们可以编写反映代码,爬上继承链,找到实现Iterable (或子接口)的类,然后向下爬,直到匹配相应的类型参数。 Unfortunately I can't think if how this would be implemented. 不幸的是,我想不出这是如何实现的。 Classes are free to declare type parameters with any name and in any order they want, so naive matching based on name or position is out. 类可以自由地声明具有任何名称的类型参数以及他们想要的任何顺序,因此基于名称或位置的天真匹配是不对的。 Maybe somebody else can contribute a solution. 也许其他人可以提供解决方案。

I don't think that you can get the generic parameter type from just an instance: 我不认为你可以从一个实例获得泛型参数类型:

new ArrayList<Integer>();

If you had a field holding the instance you can get it from there: 如果你有一个持有实例的字段,你可以从那里得到它:

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

See this similar question: Get generic type of java.util.List 看到这个类似的问题: 获取java.util.List的泛型类型

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用Java反射检查ParameterizedType参数是否扩展了特定的类 - How to use Java reflection to check if ParameterizedType argument extends a specific class 是否等于Java类中的实现,该类实现了扩展Iterable的接口? - Equals implementation in a java-class which implements an interface that extends Iterable? java,泛型 - 如何使用类型T对类进行参数化,其中BOTH:扩展并实现某些类型 - java, generics - how to parametrize a class with a type T which BOTH: extends and implements sth 给定`T`和`U`,其中`T延伸U`如何返回&#39;U` - Given `T` and `U` where `T extends U` how to return a `U` Java 1.7:Iterable的总和<T extends Number> - Java 1.7: Sum of Iterable<T extends Number> Java-如何正确使用扩展/实现 - Java - How to properly use Extends/Implements 如何检查给定字符串是否是Java中任何给定枚举的一部分? - How to check if a given string is a part of any given Enum in Java? 为什么我们不能编写类Child来实现AnyInterface扩展Java中的Base? - Why we can't write class Child implements AnyInterface extends Base in Java? 在java中扩展类并实现接口 - extends class and implements interface in java 引用实现两个或多个给定接口的任何类的对象 - Reference to an object of any class that implements two or more given interfaces
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM