获取 java.util.List 的泛型类型

     2023-03-31     157

关键词:

【中文标题】获取 java.util.List 的泛型类型【英文标题】:Get generic type of java.util.List 【发布时间】:2009-12-21 21:08:43 【问题描述】:

我有;

List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();

有没有(简单的)方法来检索列表的泛型类型?

【问题讨论】:

能够以编程方式检查 List 对象并查看其泛型类型。一个方法可能希望根据集合的泛型类型插入对象。这在运行时而不是编译时实现泛型的语言中是可能的。 对——关于允许运行时检测的唯一方法是子类化:您实际上可以扩展泛型类型,然后使用反射查找子类型使用的类型声明。这是相当多的反思,但可能。不幸的是,没有简单的方法来强制必须使用泛型子类。 肯定 stringList 包含字符串和 integerList 整数?为什么要让它变得更复杂? 【参考方案1】:

如果这些实际上是某个类的字段,那么您可以通过一点反射来获得它们:

package test;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

public class Test 

    List<String> stringList = new ArrayList<String>();
    List<Integer> integerList = new ArrayList<Integer>();

    public static void main(String... args) throws Exception 
        Field stringListField = Test.class.getDeclaredField("stringList");
        ParameterizedType stringListType = (ParameterizedType) stringListField.getGenericType();
        Class<?> stringListClass = (Class<?>) stringListType.getActualTypeArguments()[0];
        System.out.println(stringListClass); // class java.lang.String.

        Field integerListField = Test.class.getDeclaredField("integerList");
        ParameterizedType integerListType = (ParameterizedType) integerListField.getGenericType();
        Class<?> integerListClass = (Class<?>) integerListType.getActualTypeArguments()[0];
        System.out.println(integerListClass); // class java.lang.Integer.
    

您也可以对参数类型和方法的返回类型执行此操作。

但是,如果它们在您需要了解它们的类/方法的同一范围内,那么了解它们就没有意义,因为您已经自己声明了它们。

【讨论】:

在某些情况下这肯定是有用的。例如在无配置的 ORM 框架中。 ..可以使用Class#getDeclaredFields()获取所有字段,无需知道字段名。 BalusC:对我来说,这听起来像是一个注入框架。无论如何,这就是我的意思。 @Oleg 您的变量使用&lt;?&gt;(通配符类型)而不是&lt;Class&gt;(类类型)。将您的&lt;?&gt; 替换为&lt;Class&gt; 或任何&lt;E&gt; 即使在 10 年后仍然有用【参考方案2】:

您也可以对方法参数执行相同的操作:

Method method = someClass.getDeclaredMethod("someMethod");
Type[] types = method.getGenericParameterTypes();
//Now assuming that the first parameter to the method is of type List<Integer>
ParameterizedType pType = (ParameterizedType) types[0];
Class<?> clazz = (Class<?>) pType.getActualTypeArguments()[0];
System.out.println(clazz); //prints out java.lang.Integer

【讨论】:

【参考方案3】:

简短回答:不。

这可能是重复的,目前找不到合适的。

Java 使用一种称为类型擦除的东西,这意味着在运行时两个对象是等价的。编译器知道列表包含整数或字符串,因此可以维护类型安全的环境。此信息在运行时会丢失(基于对象实例),并且列表仅包含“对象”。

您可以了解该类的一些信息,它可能被参数化的类型,但通常这只是扩展“对象”的任何东西,即任何东西。如果您将类型定义为

class <A extends MyClass> AClass ....

AClass.class 将只包含参数 A 受 MyClass 限制的事实,但除此之外,没有办法分辨。

【讨论】:

如果没有指定泛型的具体类则为真;在他的示例中,他明确将列表声明为List&lt;Integer&gt;List&lt;String&gt;;在这些情况下,类型擦除不适用。【参考方案4】:

集合的泛型类型只有在它实际上包含对象时才有意义,对吧?那么这样做不是更容易吗:

Collection<?> myCollection = getUnknownCollectionFromSomewhere();
Class genericClass = null;
Iterator it = myCollection.iterator();
if (it.hasNext())
    genericClass = it.next().getClass();

if (genericClass != null)  //do whatever we needed to know the type for

运行时不存在泛型类型,但运行时内部的对象保证与声明的泛型类型相同,因此只需在处理项目之前测试项目的类就足够简单了。

您可以做的另一件事是简单地处理列表以获取正确类型的成员,忽略其他成员(或以不同方式处理它们)。

Map<Class<?>, List<Object>> classObjectMap = myCollection.stream()
    .filter(Objects::nonNull)
    .collect(Collectors.groupingBy(Object::getClass));

// Process the list of the correct class, and/or handle objects of incorrect
// class (throw exceptions, etc). You may need to group subclasses by
// filtering the keys. For instance:

List<Number> numbers = classObjectMap.entrySet().stream()
        .filter(e->Number.class.isAssignableFrom(e.getKey()))
        .flatMap(e->e.getValue().stream())
        .map(Number.class::cast)
        .collect(Collectors.toList());

这将为您提供其类是Number 子类的所有项目的列表,然后您可以根据需要对其进行处理。其余项目被过滤到其他列表中。因为它们在地图中,您可以根据需要处理它们,或忽略它们。

如果你想完全忽略其他类的项目,它会变得更简单:

List<Number> numbers = myCollection.stream()
    .filter(Number.class::isInstance)
    .map(Number.class::cast)
    .collect(Collectors.toList());

您甚至可以创建一个实用方法来确保列表仅包含与特定类匹配的那些项目:

public <V> List<V> getTypeSafeItemList(Collection<Object> input, Class<V> cls) 
    return input.stream()
            .filter(cls::isInstance)
            .map(cls::cast)
            .collect(Collectors.toList());

【讨论】:

如果你有一个空集合?或者,如果您有一个带有 Integer 和 String 的 Collection 类的约定可以只要求传入最终对象的列表,并且如果列表为 null、空或列表类型无效,则方法调用将返回 null。跨度> 在空集合的情况下,在运行时分配给任何列表类型都是安全的,因为其中没有任何内容,并且在发生类型擦除后,List is a List is a List。因此,我想不出空集合的类型会很重要的任何实例。 好吧,如果您在线程中保留引用并在对象开始出现在生产者消费者场景中时对其进行处理,那么这“可能很重要”。如果列表中有错误的对象类型,可能会发生坏事。我想是为了防止您必须通过休眠来停止处理,直到列表中至少有一项才能继续。 如果您有这种生产者/消费者场景,那么在不确定可以放入列表中的内容的情况下计划列表具有某种通用类型,无论如何都是糟糕的设计。相反,您将其声明为 Objects 的集合,当您将它们从列表中拉出时,您会根据它们的类型将它们提供给适当的处理程序。【参考方案5】:

查找一个字段的通用类型:

((Class)((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0]).getSimpleName()

【讨论】:

【参考方案6】:

如果您需要获取返回类型的泛型类型,当我需要在返回Collection 的类中查找方法然后访问它们的泛型类型时,我使用了这种方法:

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;

public class Test 

    public List<String> test() 
        return null;
    

    public static void main(String[] args) throws Exception 

        for (Method method : Test.class.getMethods()) 
            Class returnClass = method.getReturnType();
            if (Collection.class.isAssignableFrom(returnClass)) 
                Type returnType = method.getGenericReturnType();
                if (returnType instanceof ParameterizedType) 
                    ParameterizedType paramType = (ParameterizedType) returnType;
                    Type[] argTypes = paramType.getActualTypeArguments();
                    if (argTypes.length > 0) 
                        System.out.println("Generic type is " + argTypes[0]);
                    
                
            
        

    


这个输出:

泛型是类 java.lang.String

【讨论】:

【参考方案7】:

扩展 Steve K 的答案:

/** 
* Performs a forced cast.  
* Returns null if the collection type does not match the items in the list.
* @param data The list to cast.
* @param listType The type of list to cast to.
*/
static <T> List<? super T> castListSafe(List<?> data, Class<T> listType)
    List<T> retval = null;
    //This test could be skipped if you trust the callers, but it wouldn't be safe then.
    if(data!=null && !data.isEmpty() && listType.isInstance(data.iterator().next().getClass())) 
        @SuppressWarnings("unchecked")//It's OK, we know List<T> contains the expected type.
        List<T> foo = (List<T>)data;
        return retval;
    
    return retval;

Usage:

protected WhateverClass add(List<?> data) //For fluant useage
    if(data==null) || data.isEmpty()
       throw new IllegalArgumentException("add() " + data==null?"null":"empty" 
       + " collection");
    
    Class<?> colType = data.iterator().next().getClass();//Something
    aMethod(castListSafe(data, colType));


aMethod(List<Foo> foo)
   for(Foo foo: List)
      System.out.println(Foo);
   


aMethod(List<Bar> bar)
   for(Bar bar: List)
      System.out.println(Bar);
   

【讨论】:

与史蒂夫 K 的回答相同的问题:如果列表为空怎么办?如果列表是包含字符串和整数的 类型怎么办?您的代码意味着如果列表中的第一项是字符串,并且根本没有整数,则只能添加更多字符串... 如果列表是空的,那你就不走运了。如果列表是 Object 并且它实际上包含一个 Object 你没问题,但如果它有混合的东西你就不走运了。擦除意味着您可以做到这一点。在我看来,擦除是缺乏的。它的后果立即被人们所知甚少,不足以解决典型关注的有趣和期望的用例。没有什么可以阻止创建一个可以询问它需要什么类型的类,但这根本不是内置类的工作方式。集合应该知道它们包含什么,但是唉......【参考方案8】:

在运行时,不,你不能。

但是通过反射可以访问类型参数。试试

for(Field field : this.getDeclaredFields()) 
    System.out.println(field.getGenericType())

getGenericType() 方法返回一个 Type 对象。在这种情况下,它将是ParametrizedType 的一个实例,它又具有方法getRawType()(在这种情况下将包含List.class)和getActualTypeArguments(),它将返回一个数组(在这种情况下,长度为 1,包含 String.classInteger.class)。

【讨论】:

它是否适用于方法接收的参数而不是类的字段? @opensas 可以使用method.getGenericParameterTypes() 获取方法参数的声明类型。 这一直有效,直到您拥有class Foo&lt;T&gt; public T bar; Foo&lt;Integer&gt; fooField; - 在这种情况下,fooField 的“bar”成员返回的内容将是 TypeVariable 而不是 Class。即使您和我都可以清楚地看到 T 将是 Integer,但出于某种原因,我们只能使用子类的 getGenericSuperclass() 方法从 Foo 的子类中反射性地检索该信息(然后它将像上面的示例一样返回 ParametrizedType)。 【参考方案9】:

遇到了同样的问题,但我改用了 instanceof。这样做的:

List<Object> listCheck = (List<Object>)(Object) stringList;
    if (!listCheck.isEmpty()) 
       if (listCheck.get(0) instanceof String) 
           System.out.println("List type is String");
       
       if (listCheck.get(0) instanceof Integer) 
           System.out.println("List type is Integer");
       
    

这涉及使用未经检查的强制转换,因此只有在您知道它是一个列表以及它可以是什么类型时才这样做。

【讨论】:

【参考方案10】:

通常不可能,因为List&lt;String&gt;List&lt;Integer&gt; 共享同一个运行时类。

不过,您可能能够反映保存列表的字段的声明类型(如果声明的类型本身不引用您不知道其值的类型参数)。

【讨论】:

【参考方案11】:
import org.junit.Assert;
import org.junit.Test;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class GenericTypeOfCollectionTest 
    public class FormBean 
    

    public class MyClazz 
        private List<FormBean> list = new ArrayList<FormBean>();
    

    @Test
    public void testName() throws Exception 
        Field[] fields = MyClazz.class.getFields();
        for (Field field : fields) 
            //1. Check if field is of Collection Type
            if (Collection.class.isAssignableFrom(field.getType())) 
                //2. Get Generic type of your field
                Class fieldGenericType = getFieldGenericType(field);
                //3. Compare with <FromBean>
                Assert.assertTrue("List<FormBean>",
                  FormBean.class.isAssignableFrom(fieldGenericType));
            
        
    

    //Returns generic type of any field
    public Class getFieldGenericType(Field field) 
        if (ParameterizedType.class.isAssignableFrom(field.getGenericType().getClass())) 
            ParameterizedType genericType =
             (ParameterizedType) field.getGenericType();
            return ((Class)
              (genericType.getActualTypeArguments()[0])).getSuperclass();
        
        //Returns dummy Boolean Class to compare with ValueObject & FormBean
        return new Boolean(false).getClass();
    

【讨论】:

找不到getFieldGenericTypefieldGenericType是什么【参考方案12】:

正如其他人所说,唯一正确的答案是不,类型已被删除。

如果列表的元素数量非零,您可以调查第一个元素的类型(例如,使用它的 getClass 方法)。这不会告诉您列表的泛型类型,但可以合理地假设泛型类型是列表中类型的某个超类。

我不提倡这种方法,但在绑定中它可能有用。

【讨论】:

如果没有指定泛型的具体类则为真;在他的示例中,他明确将列表声明为List&lt;Integer&gt;List&lt;String&gt;;在这些情况下,类型擦除不适用。【参考方案13】:

一个小小的帮助方法:

/**
 * Get type of collection field.
 *
 * @param aClass A class containing collection.
 * @param collectionName A collection field name.
 */
@SneakyThrows
public static Class<?> getCollectionType(Class<?> aClass, String collectionName) 
  Field field = aClass.getDeclaredField(collectionName);
  ParameterizedType genericType = (ParameterizedType) field.getGenericType();
  return (Class<?>) genericType.getActualTypeArguments()[0];

【讨论】:

【参考方案14】:

使用反射获取Field,然后你可以这样做:field.genericType 获取包含泛型信息的类型。

【讨论】:

考虑添加一些示例代码,否则这更适合评论而不是答案

获取运行时的泛型类型

publicclassBaseDaoImpl<T>extendsHibernateDaoSupportimplementsBaseDao<T>{privateClassclazz;//用于接收运行期泛型类型publicBaseDaoImpl(){//获得当前类型的带有泛型类型的父类ParameterizedTypepd=(ParameterizedType)this.get 查看详情

java示例代码_获取Jackson ObjectMapper的泛型类型

java示例代码_获取Jackson ObjectMapper的泛型类型 查看详情

Firebase:类 java.util.List 具有泛型类型参数

】Firebase:类java.util.List具有泛型类型参数【英文标题】:Firebase:Classjava.util.Listhasgenerictypeparameters【发布时间】:2018-04-1616:18:01【问题描述】:我正在为我的android应用程序使用firebase,并将我自己的用户对象存储在数据库中。但... 查看详情

获取在接口或者类上定义的泛型类型

通过Class类上的 getGenericSuperclass()或者 getGenericInterfaces()获取父类或者接口的类型,然后通过ParameterizedType.getActualTypeArguments()可以得到定义在类或者接口上的泛型类型,具体参考如下代码:packagecom.jiaoyiping.event;/**CreatedwithInte 查看详情

java怎么获取一个泛型方法的真实泛型类型

不知道我对你的题目的理解对不对:有个方法返回一个泛型,你想去这个泛型的类型是不是?package test;import java.util.ArrayList;import java.util.List;public class Test01  public static void main(String[] args) ... 查看详情

通过给定的泛型类型 Scala 获取类的伴随对象

】通过给定的泛型类型Scala获取类的伴随对象【英文标题】:GetcompanionobjectofclassbygivengenerictypeScala【发布时间】:2012-02-2816:12:40【问题描述】:我要做的是创建一个函数,该函数将采用泛型类并在其中使用静态方法(对不起,Java... 查看详情

在 Jackson 的 JsonDeserializer 中获取检测到的泛型类型

】在Jackson的JsonDeserializer中获取检测到的泛型类型【英文标题】:GetthedetectedgenerictypeinsideJackson\'sJsonDeserializer【发布时间】:2018-05-0101:49:16【问题描述】:由于外部原因,我系统中的所有javaMaps只能作为来自客户端的键值对的列... 查看详情

GetMethod 的 Type[] 以获取具有两个参数和一个类型参数的泛型方法

】GetMethod的Type[]以获取具有两个参数和一个类型参数的泛型方法【英文标题】:Type[]forGetMethodtogetgenericmethodwithtwoargumentsandonetypeparameter【发布时间】:2021-12-1822:54:39【问题描述】:存在许多如何接收泛型方法的变体:通过LINQ等在... 查看详情

集合中的泛型

...泛型时,任何类型都可以添加进集合,类型不安全)解决获取数据元素时,需要类型强转的问题(读取集合元素强转时很繁琐,可能会报ClassCastException)关于以上两点的说明:publicclassTestGeneric{ @Test publicvoidtest(){ Listlist=newArrayLis... 查看详情

java如何获得list的泛型类型

...索引).getClass()实际上,这个就相当于MainFormList.class这样就获取到了你所get出来的元素的泛型类型了,大多是反射的时候在需要获取。。。希望可以帮助到你 参考技术B从aa中取出一个后,用类型转换比如:MainFormListmf=(MainFormList)aa.... 查看详情

转换为协议默认方法的泛型类型

...发布时间】:2020-12-0622:55:10【问题描述】:我正在尝试为获取请求创建一个具有默认实现的协议。我遇到的问题是它应该返回的类型需要是通用的,而不是从协议中设置的东西,而是在调用它的东西上。我真正需要的是一个方法... 查看详情

trait 的泛型类型和泛型关联类型之间有啥区别?

】trait的泛型类型和泛型关联类型之间有啥区别?【英文标题】:What\'sthedifferencebetweenatrait\'sgenerictypeandagenericassociatedtype?trait的泛型类型和泛型关联类型之间有什么区别?【发布时间】:2019-07-1411:02:50【问题描述】:这个问题是... 查看详情

返回另一个泛型类型的泛型类型

】返回另一个泛型类型的泛型类型【英文标题】:Returnagenerictypeofanothergenerictype【发布时间】:2021-12-2512:29:27【问题描述】:我有一个自定义枚举类:publicabstractclassEnum<T>whereT:struct,IComparablepublicTIdget;publicstringValueget;protectedEnu... 查看详情

java中如何去除list中的重复的值

直接放入set中。如果List的泛型是基本类型(封装类)或String,可以直接这样做。但是泛型是你自己写的类,就需要你把这个类重写equals和hashCode方法。import java.util.ArrayList;import java.util.LinkedHashSet;import java.util.List;import&... 查看详情

获取类声明的泛型的工具类(代码片段)

记录一下适用于找类似如下声明中的泛型,T和E的值classA<TextendsB,EextendsC>classCextendsA<T1,E1>classDextendsC该工具支持遍历也就是,可以在D中寻找到T1和E1的类型importjava.lang.reflect.ParameterizedType;importjava.lang.refl 查看详情

java中的泛型

泛型的概念泛型:  泛型是一种末知的数据类型,当我们不知道使用什么数据类型的时候,可以使用泛型  泛型也可以看成是一个变量用来接收数据类型  Ee:Element元素  Tt:Type类型是否使用泛型的对比不使用泛型/***... 查看详情

关于ts的泛型

1.泛型是什么泛型,顾名思义,就是可以适用于多个类型,使用类型变量比如T帮助我们捕获传入的类型,之后我们就可以继续使用这个类型。如下定义了一个identity泛型函数,<T>添加了类型变量T,它就可以帮助捕获我们传入... 查看详情

只能采用某些类型的泛型类

】只能采用某些类型的泛型类【英文标题】:Agenericclasswhichcantakeonlycertaintypes【发布时间】:2018-06-2718:29:21【问题描述】:假设,我想创建一个只能将int和double作为类型的泛型类。publicclassA<T>whereT:int,doublepublicTpropertyget;set;例... 查看详情