将具有类型参数的类作为Java中泛型方法的类型参数传递

Posted

技术标签:

【中文标题】将具有类型参数的类作为Java中泛型方法的类型参数传递【英文标题】:Passing a class with type parameter as type parameter for generic method in Java 【发布时间】:2013-08-27 17:14:32 【问题描述】:

问题总结: 我想将带有类型参数的类(例如ArrayList<SomeClass>)作为类型参数传递给泛型方法。

假设我有一个方法:

    public static <T> T getGenericObjectFromJson(String json, Class<T> genericType)
        // details unimportant, basically returns an object of specified type
        return JsonParser.fromJson(json, genericType); 
    

当然,这种方法对于任何类型的课程都非常适用。我可以这样调用方法,例如:

getGenericObjectFromJson(jsonString, User.class)

问题:我发现我不能这样做:

getGenericObjectFromJson(jsonString, ArrayList<User>.class)

从语法上看,这显然是无效的。但是,我不确定我将如何完成这样的事情。当然,我可以通过 ArrayList.class,但是泛型类型的添加使其在语法上不再有效,我想不出办法。

唯一直接的解决方案是这样的(这似乎很愚蠢):

getGenericObjectFromJson(jsonString, new ArrayList<User>().getClass())

然而,无论如何我们最终都会丢失泛型类型,而只是取回一个未知类型的 ArrayList(尽管它可以强制转换)。另外,不必要地实例化一个对象。

到目前为止,我唯一的解决方案是将该方法包装在一个包含可以实例化的泛型类型参数的类中,如下所示:

public class JsonDeserializer<T>...

在这种情况下,getGenericObjectFromJson 方法将使用类的泛型类型。

问题:最后,我很好奇为什么我不能传递带有类型参数的类,以及是否有办法完成我试图做的事情。

一如既往,如果这个问题有任何问题,请告诉我。

【问题讨论】:

由于类型擦除,我认为这是不可能的。 Java 泛型是一种编译时构造,在运行时不存在,因此不存在代表构造的泛型类型的 Class 对象。 解决方案:C#,开个玩笑。你不能这样做,因为“类型擦除”,这是你编译时 java 所做的,通过删除泛型类型信息 将其重铸为getGenericArrayListFromJson(jsonString, User.class) 会起作用吗?这可能是你能做到的最好的了。 实际上,这在 Java 中是可能的。泛型类型信息在运行时并没有完全丢失——您可以访问父类或接口上的泛型类型信息!在下面检查我的答案。 @asgs 已经很久了,但是是的,这可能就是我的意思。将更新以避免混淆任何人。 【参考方案1】:

这在 Java 中实际上是可能的,使用一些“技巧”。不要屈服于 C# ***分子的压力! (j/k)

“诀窍”是创建一个扩展泛型类型的类,并通过.getGenericSuperclass().getGenericInterfaces()返回的Type访问父类的类型参数的值。

这很麻烦。为了简化我们的生活,Google 已经为我们编写了大部分无聊的代码,并通过 Guava 提供。

检查 TypeToken 类,它完全符合您的要求。例如:

TypeToken<List<String>> stringListTok = new TypeToken<List<String>>() ;

然后你传递一个TypeToken&lt;T&gt; 而不是Class&lt;T&gt;,仅此而已。它为您提供了对 T 表示的类型进行反射的方法。

内部所做的只是简单地调用.getClass().getGenericSuperclass()(或...Interfaces()),然后从TypeParameterizedType 进行一些丑陋的转换,并从那里检索所有信息(.getActualTypeArguments() 等)。

最后,如果你想用依赖注入做类似的事情(即假设你需要在一个类的构造函数上注入一个Class&lt;T&gt;,或者你想获得某个参数化接口的实例,其中的实例应该取决于类型参数),Google Guice(来自 Google 的 DI 容器)有一个非常相似的机制来解决这个问题,称为TypeLiteral。使用和幕后代码与 Guava 的TypeToken 几乎相同。在这里查看:TypeLiteral

【讨论】:

哇,真是太棒了。太有趣了,我实际上正在使用谷歌 Gson 的库,它使用(或至少包含部分)番石榴,但我显然没有完全理解 TypeToken 是如何工作的。非常感谢,非常有用的信息。 有人可以详细说明第一种方式。 AFAIK,您不能在 java 中扩展泛型类型。 @Kedarnath, class A&lt;T&gt; class B extends A&lt;Integer&gt; --> 这绝对没问题。这就是你在说的吗?你能提供一个你声称不可能的例子吗? 对于没有番石榴依赖但有杰克逊的任何人,com.fasterxml.jackson.core.type.TypeReference 是模拟的。

以上是关于将具有类型参数的类作为Java中泛型方法的类型参数传递的主要内容,如果未能解决你的问题,请参考以下文章

Java中泛型的使用

java如何获得泛型中的类

Java中泛型的理解

Java中泛型 类型擦除

Java中泛型与集合

Java基础Java中如何获取一个类中泛型的实际类型