【问题标题】:Given a Collection get the type of the items (Java)给定一个集合获取项目的类型(Java)
【发布时间】:2013-02-19 14:59:04
【问题描述】:

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

classUnderTest.getName()

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

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

【问题讨论】:

  • 你为什么需要那个?你能举个例子解释一下吗?

标签: java oop reflection introspection


【解决方案1】:

一般来说,你不能这样做。例如,该集合可能是空的或仅由空值组成。另外,接口呢?可能存在所有集合元素共有的多个接口。

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

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 是类层次结构中集合项的最大深度。

【讨论】:

    【解决方案2】:

    答案从大多数人所说的帖子中非常明显......那就是 由于Type Erasure,您无法在运行时获取/推断集合的泛型类型

    但是,我想证明它的含义

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

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

    但是,如果我们把,

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

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

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

    这是运行时类型擦除引发的异常

    【讨论】:

      【解决方案3】:

      简短的回答是你不能。

      泛型类型参数不记录为该类型实例(对象)的一部分。 (阅读“type erasure”...)此外,您的示例仍然使用原始类型。

      您可以选择(非空)集合中的某些元素并获取其类型,但它不会告诉您有关其他元素类型的任何信息。 (它们可能相同......或不同。)如果集合为空或包含 null 引用,您甚至无法可靠地做到这一点。

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

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

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

      【讨论】:

        【解决方案4】:

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

        但您可以遍历 Collection 并获得最通用的类​​型。但即使你这样做了,你也无法将该类型作为泛型类型传递。

        您最好的选择是禁止警告并确保没有任何非法内容被传递到方法中。

        【讨论】:

          【解决方案5】:

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

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

          【讨论】:

            【解决方案6】:

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

            但是,您真的需要知道集合项的类型吗?

            【讨论】:

            • 是的,我需要,因为我正在为不与继承链接的不同类创建一些测试方法。
            • 然后有一个Map&lt;Class,Runnable&gt;(或不同的接口)与所有每个类的测试用例,并运行各种项目的类。您还需要查找项目类还是一个简单的 item.getClass().getName() 就足够了(对于每个项目)?
            猜你喜欢
            • 2010-12-12
            • 1970-01-01
            • 2011-01-19
            • 2018-06-26
            • 2015-02-08
            • 1970-01-01
            • 2023-03-03
            • 2016-05-16
            • 1970-01-01
            相关资源
            最近更新 更多