【问题标题】:How do I build a Java type object at runtime from a generic type definition and runtime type parameters?如何在运行时从泛型类型定义和运行时类型参数构建 Java 类型对象?
【发布时间】:2012-02-02 11:57:02
【问题描述】:

假设一个泛型类型声明(Java)

class Foo<T> {
    public T bar;
}

如何在运行时实例化一个 Type 对象,该对象表示 Foo 在特定类型 T 上的参数化(也仅在运行时才知道)?

【问题讨论】:

  • 为什么需要它?类型擦除使这个想法变得毫无意义,因此请尝试解释您要实现的目标。
  • 我需要它以便在从 JSON 字符串反序列化时为(反)序列化库(在本例中为 google gson)提供一个表示我预期类型的​​类型对象。擦除不会使其毫无意义,因为类型对象将在运行时进行检查。
  • “我使用的库需要将字段视为运行时提供的类型”,“表示我预期类型的​​类型对象”这是什么意思?你能详细说明你想做什么吗?
  • @Viruzzo,我需要使用反射(在本例中为 gson)告诉库,我希望它从 JSON 字符串实例化填充特定类型的对象。 Gson 将使用反射来检索字段栏的类型,我想控制该类型将被视为什么以指导反序列化。

标签: java generics reflection


【解决方案1】:

我想我理解你的问题。你想序列化一个Foo&lt;T&gt;,并且在运行时你有T的类对象(但它在编译时不是固定的)。因此,Gson 中建议的创建TypeToken 的匿名子类的解决方案不起作用,因为这需要在编译时对参数化类型(例如Foo&lt;String&gt;)进行硬编码,如果您使用类似Foo&lt;T&gt;.

不过,让我们看看 Gson 网站上的 TypeToken 方法实际完成了什么。您创建一个TypeToken 的匿名子类的对象,然后使用其getType() 方法请求其类型参数。类的超类是其元数据的一部分,并包括其超类的通用参数。因此在运行时,它可以查看自己的继承层次结构,并找出您为TypeToken 使用的类型参数,然后为该类型返回一个java.lang.reflect.Type 实例(如果它被参数化,它将是一个@987654332 @ 实例)。一旦你得到这个Type 实例,你应该将它作为toGson() 的第二个参数传递。

我们需要做的就是找到另一种方法来创建ParameterizedType 的这个实例。 ParameterizedType 是一个接口,但不幸的是,公共 Java API 没有提供任何具体的实现或任何动态创建ParameterizedType 的方法。在您可以使用的私有 Sun API 和 Gson 代码中似乎有一个名为 ParameterizedTypeImpl 的类 (e.g. here)。您可以简单地复制代码并将其重命名为您自己的类。然后,要在运行时创建一个代表Foo&lt;String&gt;Type 对象,您可以执行new ParameterizedTypeImpl(Foo.class, new Type[]{String.class}, null) 之类的操作(未经测试)

【讨论】:

  • 太棒了,你似乎是唯一一个得到它的人,而且你有一个具体的解决方案。我很惊讶没有“官方”的方式来做到这一点,但这种方法绝对足以让它与 gson 配合得很好,这就是我所需要的。谢谢!
  • 嗯,有一个简单的原因......这不是你所要求的。它是 Gson 如何解析的提示,而不是创建类型的方法。
  • 我正在寻找这个在生成 Spring bean 时使用,但我需要构建一个 Class> 实例 => MyGenericType.class Spring 只会采用 Class>,是可以让它包含一个带有绑定参数的特定泛型类型吗?
  • 我还在 Guava、Apache Commons-Lang、Spring-Core、Hibernate Validator 中找到了一个实现(实际上是 Sun API 中的两个)。
【解决方案2】:

例如,假设我们正在谈论List&lt;String&gt;
你可以构造这样的东西:

    Type type = new ParameterizedType() {
            public Type getRawType() {
                return List.class;
            }

            public Type getOwnerType() {
                return null;
            }

            public Type[] getActualTypeArguments() {
                return new Type[] { String.class};
            }
        };

如果你需要这个从 JSON 反序列化,你可以使用这个 Type 调用 gson.fromJson

【讨论】:

  • 唯一对我有用的解决方案,而不是String.class 我有我的clazz 参数,其中最后一个参数类型为Class&lt;T&gt;
【解决方案3】:

类型擦除的常用方法是:

class Foo<T> {

    Class<T> clazz;

    public Foo(Class<T> c) {
        clazz = c;
    }

    public T bar {
        return clazz.newInstance();
    }
}

如果你的 T 没有无参数构造函数,你可以在 Class 对象上使用反射来做一些更有趣的事情;一旦你有了Class&lt;T&gt; 的实例,你就可以获得一个实例。

我在使用 gson 时也遇到了这个问题。我最终得到了这个:

public class JsonWrapper {
    private String className;
    private String json; // the output of the gson.toJson() method
}

当我需要反序列化时,我做了一个Class.forName(className),然后我就拥有了调用 gson 库的 fromJson() 方法所需的一切。

我不敢相信 gson 不支持本机 - 这似乎是一件显而易见的事情......获取一些 json 并将其转换为对象 不知道它是哪个类是事先。

【讨论】:

  • 虽然我不需要简单地实例化在运行时确定的类型,但我需要创建一个保留类型信息的 Type 对象。正如 Jon 所指出的,类型信息并不总是丢失。一种可能的方法是创建一个新类型,使用提供的参数动态地继承我的泛型类型?我实际上并不需要它来表示 Foo,只要它可以分配给 Foo...
  • 至于您的 gson 示例,这也是我的解决方法,但如果可以避免它会更好。我的情况略有不同,因为我在反序列化之前确实知道预期的类型,但我正在构建一个通用基类来处理共享一些整体结构但有效负载布局不同的消息......
【解决方案4】:

Gson 确实为此提供了解决方案:https://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener

当然,这只是 Bohemian 的解决方案(您仍然需要以某种方式传递类型参数),而是自动为您完成。

【讨论】:

  • 我已经发送了这个,但它仍然假设您在编译时知道类型,但对我来说并非如此。为了实例化一个“TypeToken”,我需要在运行时实例化一个带有类型参数的开放泛型类型,如何做到这一点是我最初的问题......
  • 为此,您需要 Bohemian 的解决方案,即使用在构造函数中初始化的 Class 类成员(或等效的 String)。擦除没有干净的方法,我见过的所有其他方法都更加烦躁。
  • 所以,我确实有一个代表 T 的实际类型的 Class 对象,但这仍然不足以告诉 gson 我希望命名属性属于这种类型。
  • 假设上面的文档的例子,你会在反序列化时做这样的事情Type fooType = new TypeToken&lt;Foo&lt;Class.forName(className)&gt;&gt;() {}.getType(); gson.fromJson(json, fooType);,其中className是一个带有类型名称的字符串。
  • 那行不通...我确实有 Class 实例,这不是我的问题。
【解决方案5】:

其他人所说的:)。您要实例化的类需要在运行时可用。这样做的方法有很多:将类或类名放在工厂本地的变量中,拥有一个受保护的方法,如果您需要在许多不同的地方执行此操作,则创建一个“对象工厂”类等。这是一种bean 框架所做的工作,所以如果您正在使用一个,可以通过配置来完成。

【讨论】:

  • 不,这根本不能回答我的问题。我确实有一个用于类型参数的 Class 对象,这不是在运行时获取泛型类型参数的 Class 的问题,而是在运行时实例化泛型 Type 实例的问题。
【解决方案6】:

这似乎比大多数人想象的要简单:

Type collectionType = new TypeToken<ArrayList<Person>>(){}.getType();
ArrayList<Person> persons = gson.fromJson(response, collectionType);

无需按照 newacct 的建议复制 ParameterizedTypeImpl 类。

【讨论】:

  • -1 如果可以的话; Freed 明确表示 T 仅在运行时已知。因此,这个解决方案对他不起作用,正如 newacct 在他的回答中所解释的那样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-19
  • 2016-05-17
  • 1970-01-01
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多