在运行时指定通用集合类型参数 (Java 反射)

5

我希望在运行时使用反射获取集合的泛型类型。

代码(JAVA):

Field collectionObject = object.getClass().getDeclaredField(
    collectionField.getName());
//here I compare to see if a collection
if (Collection.class.isAssignableFrom(collectionObject.getType())) {
   // here I have to use the generic type of the collection 
   // to see if it's from a specific type - in this case Persistable
   if (Persistable.class.isAssignableFrom(GENERIC_TYPE_COLLECTION.class)) {
   }
}

有没有办法在Java运行时获取集合的泛型类型? 在我的情况下,我需要集合泛型类型的.class。
提前感谢!

我曾经遇到过类似的问题,以下是最终解决方案:https://dev59.com/sHVC5IYBdhLWcg3wykaj#183232 - Zds
4个回答

17

类型擦除意味着在执行时,关于对象的泛型类型信息并不存在。

(该链接是Angelika Langer的Java Generics FAQ相关章节,几乎可以回答您可能提出的所有有关Java泛型的问题:)

然而,您真正感兴趣的是字段的类型,而不是对象的类型。我误读了问题,尽管答案已被接受,但我希望通过现在进行修复来弥补错误 :)

如果字段本身不使用类型参数,则可以完成此操作。例如:

import java.lang.reflect.*;
import java.util.*;

public class Test
{
    public List<String> names;

    public static void main(String [] args)
        throws Exception // Just for simplicity!
    {
        Field field = Test.class.getDeclaredField("names");

        ParameterizedType type = (ParameterizedType) field.getGenericType();

        // List
        System.out.println(type.getRawType());

        // Just String in this case
        for (Type typeArgument : type.getActualTypeArguments())
        {
            System.out.println("  " + typeArgument);
        }
    }
}

如果字段在类T中,字段为List<T>,那么您需要知道实例的类型参数才能知道集合的类型参数。
将其翻译成所需代码有些棘手 - 您实际上需要在集合类的点上知道类型参数。例如,如果某人声明:
public class StringCollection implements Collection<String>

并且,如果有一个类型为StringCollection的字段,该字段本身不会有任何类型参数。然后,您需要递归检查 getGenericSuperTypegetGenericInterfaces,直到找到所需内容。

虽然这是可能的,但这确实不容易。如果我是你,我会尝试改变你的设计,使你不需要这个。


1
递归检查确实不容易,需要考虑许多边角情况。而这只是第一步,接下来你需要一个处理泛型的isAssignableFrom等效方法。我不建议尝试自己实现它。我编写了一个名为gentyref的库,可以为您完成此操作。请参见http://code.google.com/p/gentyref/。 - Wouter Coekaerts

9
你可以完全按照自己的意愿进行操作。类型擦除意味着你无法检查集合实例的泛型类型,但是你可以检查字段的泛型类型。
class Thing {
  List<Persistable> foo;
}


Field f = Thing.class.getDeclaredField("foo");
if( Collection.class.isAssignableFrom( f.getType() ) {
   Type t = f.getGenericType();
   if( t instanceof ParameterizedType ) {
     Class genericType = (Class)((ParameterizedType)t).getActualTypeArguments()[0];
     if( Persistable.class.isAssignableFrom( genericType ) )
         return true;
   }
}

这里有很多可能出错的地方,例如,如果您拥有

Class Thing<T> {
  List<T> foo;
}

如果是这样,上述方法将无效。

1
我猜这一行代码应该是:Class genericType = (Class)((ParameterizedType)t).getActualTypeArguments()[0];而不是Class genericType = (Class)((ParameterizedType)t).getActualTypeArguments[0];是吧? - andersonbd1

0

从我的帖子中复制:https://dev59.com/4HNA5IYBdhLWcg3wVcP6#1005283

我在几个项目中使用了类似于他在这里解释的解决方案,发现它非常有用。

http://blog.xebia.com/2009/02/07/acessing-generic-types-at-runtime-in-java/

这个问题的要点是在运行时使用以下内容来确定类型参数:

public Class returnedClass {
  ParameterizedType parameterizedType =
    (ParameterizedType) getClass().getGenericSuperClass();
 return (Class) parameterizedtype.getActualTypeArguments()[0];
}

0

您可以使用Field.getGenericType获取从对象读取的字段的通用类型。请注意,该字段可能是原始类型,也可能是罕见类型(泛型但具有原始泛型参数),使用实例的类型,使用通配符等。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接