给定一个Collection获取项目的类型(Java)

时间:2013-03-05 09:16:04

标签: java oop reflection introspection

我需要获取集合项的类型。 为了检索单个实例的类,我使用以下代码:

classUnderTest.getName()

但是,我如何检索以下集合的项目类?

Collection collection = (Collection) getCollection.invoke(instance1);

6 个答案:

答案 0 :(得分:0)

由于通用擦除,Collection对象不知道必须的项目类型,只知道它们现在是什么类型。根据您的需要,一个解决方案可能是以艰难的方式去做,并找到集合中所有项目的最具体的超类(或超接口)。

但是,你真的需要知道收集项的类型吗?

答案 1 :(得分:0)

由于Type Erasure,您无法在运行时获取/推断集合的泛型类型。泛型用于编译时安全性,类型信息在运行时被擦除。

在运行时,实例为Object用于收集,它不关心运行时的实际类型。

答案 2 :(得分:0)

简短的回答是,你不能。

通用类型参数不会记录为该类型的实例(对象)的一部分。 (阅读“type erasure”......)此外,您的示例无论如何都在使用原始类型。

您可以选择(非空)集合的某些元素并获取其类型,但它不会告诉您有关其他元素类型的任何信息。 (它们可以是相同的......或者不同的。)如果集合是空的或包含null引用,你甚至无法可靠地执行此操作。

无论如何,这将为您提供集合的第一个元素的类型。

    Collection c = ...
    Iterator it = c.iterator();
    if (c.hasNext()) {
        Class clazz = c.next().getClass();
        ...
    }

你可以遍历整个集合并(有一些困难)找出它们都符合的公共类或接口。但即便如此,也不一定与声明的元素类型相同。

答案 3 :(得分:0)

一般来说,你不能这样做。例如,该集合可以是空的或仅由空值组成。那么,接口呢?可能有多个接口,这些接口对所有集合元素都是通用的。

但是,在您的情况下,以下其中一项可能会有所帮助:

1)分析集合内容:假设集合非空,请扫描集合中的所有项目,并在类层次结构中查找它们的最低共同祖先。

2)获取泛型声明类型:如果要检查的对象被声明为某个类的数据成员(带泛型),则可以在运行时使用反射计算其声明的泛型类型(见Field.getGenericType()

- 编辑 -

只是为了练习,我实现了最低共同的祖先实用程序:

    public static Class<?> getLCA(Collection<?> c) {
        Class<?> res = null;
        for (Object obj : c)
            if (obj != null) {              
                Class<?> clazz = obj.getClass();
                res = (res == null)? clazz : getLCA(res, clazz);
            }

        return res;
    }

    private static Class<?> getLCA(Class<?> classA, Class<?> classB) {
        if (classA.isAssignableFrom(classB))
            return classA;
        if (classB.isAssignableFrom(classA))
            return classB;

        while (!classA.isAssignableFrom(classB))
            classA = classA.getSuperclass();

        return classA;
    }

效率:O(n * h),其中n是集合大小,h是类层次结构中集合项的最大深度。

答案 4 :(得分:0)

你不能。在Java中,无法在运行时检索泛型类型。它被称为Type Erasure

但您可以迭代Collection并获得最常规的类型。但即使你这样做,也无法将该类型作为泛型传递。

你最好的选择是压制警告并确保没有任何违法行为传递给方法。

答案 5 :(得分:0)

从大多数人所说的帖子中可以看出答案很明显......就是这样 由于Type Erasure

,您无法在运行时获取/推断Collection的泛型类型

但是,我想证明它意味着什么

假设we create a class MyClass并执行此操作

Collection<String> collection = new ArrayList<String>();

public static void main(String[] args)  {

    Field stringListField = null;
    try {
        stringListField = MyClass.class.getDeclaredField("collection");
        ParameterizedType stringListType = (ParameterizedType) stringListField.getGenericType();
        Class<?> stringListClass = (Class<?>) stringListType.getActualTypeArguments()[0];
        System.out.println(stringListClass); 

    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    }
}

它将打印类java.lang.String 作为结果...这意味着它能够推断出何时 Collection对象使用正确的泛型参数Collection

但是,如果我们放,

Collection<?> collection = new ArrayList<String>();

然后它会编译得很好,但在运行时你会得到以下异常..

sun.reflect.generics.reflectiveObjects.WildcardTypeImpl cannot be cast to java.lang.Class

这是运行时类型擦除

引发的异常