当前位置: 首页 > 面试题库 >

对于任何给定的T,如何使用Java反射来检查给定的类是否实现Iterable

晏永康
2023-03-14
问题内容

我有一个特定的目标类型(在运行时确定),还有一个与之比较的可迭代类。我正在尝试编写一种检查类的泛型参数的方法,以查看它是否是可迭代对象类型的可迭代对象。例子:

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。这是例如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

您可以看到type参数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

您可以看到的type参数ArrayList已知为SomeObject,但这就是它的停止位置。类型参数之间没有连接关系。原因是这段代码:

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

getGenericInterfaces是获取接口的类型参数信息的唯一方法,但是该方法是由Class而不是声明Type。只要该方法有一个ParameterizedType实例,该实例拥有表示其子类的通用性的状态,就会被强制调用getRawType,它返回Class没有类型实参信息的单例。这是一个问题22,导致只能获得使用具体类型实参实现的接口的类型实参。

我不知道任何将类型实参与它们解析的参数匹配的反射API方法。从理论上讲,可以编写反射性代码,使其爬上继承链,找到实现的类Iterable(或子接口),然后向下爬回直到与相应的类型参数匹配。不幸的是,我不认为这将如何实现。类可以随意使用任何名称和所需的顺序声明类型参数,因此可以不进行基于名称或位置的幼稚匹配。也许其他人可以提供解决方案



 类似资料:
  • 问题内容: 好吧,这是一个复杂的问题,我完全迷失了。 假设您有一个字符串和一个通用类。像这样。 您将如何检查String是否表示该类可以相等的值。 例如,可以这样说: 如何检查字符串“ true”实际上是布尔值? 这是另一个例子。可以这样说: 如何检查字符串“ true”不是整数? 问题答案: 鉴于您只需要 Wrapper Types ,可以在此处使用一些反射技巧(为简洁起见,忽略无关代码的异常处

  • 问题内容: 我需要保留Swift元类型的集合并编写一个函数,该函数将检查给定对象是否是其中之一的实例。我可以在Java中轻松做到这一点: 但是,我不知道如何在Swift中做到这一点: 这甚至可以在Swift中完成吗? 问题答案: 不幸的是,您目前只能对运算符使用命名类型,还不能对其使用任意的元类型值(尽管实际上 应该 可以使用IMO )。 假设您可以控制要与之进行比较的元类型的创建,则达到相同结果

  • 检查给定行是否为java代码的正确方法是什么? 输入:日志支持。java:44 com/sun/activation/registries/LogSupport日志(Ljava/lang/String;)五、 预期输出:false。 输入:扫描仪输入=新扫描仪(系统输入); 预期输出:true。 我尝试了EclipseJDTASTParser来检查是否可以创建AST。代码如下: 但这是行不通的。有

  • 我有一个数组,它由组成。我想对它进行迭代,并找到所有作为数组实例的元素。 如何在Swift中检查对象是否属于给定类型?

  • 问题内容: 下面的代码不适用于某些输入。 我最初的想法是检查每个输入是否为2的幂,方法是从1开始乘以2直到超过输入数量,然后在每个步骤进行比较。相反,我预先存储了2的所有幂,以便检查中的给定输入。如何改善呢? 问题答案: 将 _ 最好的,最准确_ 的方法是使用位操作: 说明: 2的每个幂将1位恰好设置为1(该数的对数以2为底的索引中的位)。因此,当从中减去1时,该位​​翻转为0,而所有在前位翻转为

  • 我试图检查一个给定的二叉树是否是二叉查找树。我要做的是对二叉树进行有序遍历,并将当前元素与前一个元素进行比较。如果当前元素更大,我们继续进一步检查,否则给定的树是无效的。 这是我写的代码。我到底做错了什么?