如何在运行时从泛型类型定义和运行时类型参数构建 Java 类型对象?
假设有一个泛型类型声明(Java),
class Foo<T> {
public T bar;
}
我如何在运行时实例化一个 Type 对象,该对象表示通过特定类型 T (也仅在运行时已知)参数化的 Foo ?
Assuming a generic type declaration (Java)
class Foo<T> {
public T bar;
}
how can I, at runtime, instantiate a Type object that represents Foo parameterized over a specific type T (also known only at runtime)?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我想我明白你的问题。您想要序列化
Foo
,并且在运行时拥有T
的类对象(但它在编译时并未修复)。因此,Gson 中建议的创建TypeToken
匿名子类的解决方案不起作用,因为这需要将参数化类型(例如Foo
)硬编码在编译时,如果您使用类似Foo
之类的东西,它就不起作用。不过,让我们看看 Gson 站点上的 TypeToken 方法实际完成了什么。您创建
TypeToken
匿名子类的对象,然后使用其getType()
方法请求其类型参数。类的超类是其元数据的一部分,并且包括其超类的通用参数。因此在运行时,它可以查看自己的继承层次结构,并找出您用于TypeToken
的类型参数,然后返回一个java.lang.reflect.Type
实例对于该类型(如果它是参数化的,则将是一个 ParameterizedType 实例)。获得此Type
实例后,您应该将其作为toGson()
的第二个参数传递。我们需要做的就是找到另一种方法来创建这个 ParameterizedType 实例。
ParameterizedType
是一个接口,但不幸的是公共 Java API 没有提供任何具体的实现或任何动态创建ParameterizedType
的方法。在您可以使用的私有 Sun API 和 Gson 代码中似乎有一个名为ParameterizedTypeImpl
的类 (例如此处)。您只需复制代码并将其重命名为您自己的类即可。然后,要在运行时创建表示Foo
的Type
对象,您只需执行new ParameterizedTypeImpl(Foo.class, new Type[]{ String.class}, null)
(未经测试)I think I understand your question. You want to serialize a
Foo<T>
, and you have the class object ofT
at runtime (but it's not fixed at compile time). Therefore, the suggested solution in Gson of creating an anonymous subclass ofTypeToken
does not work because that requires that the parameterized type (e.g.Foo<String>
) be hard-coded at compile time, and it does not work if you use something likeFoo<T>
.However, let's look at what the
TypeToken
method on the Gson site actually accomplishes. You create an object of an anonymous subclass ofTypeToken
, and then ask for its type parameter using itsgetType()
method. A class's superclass is part of its metadata, and includes the generic parameters of its superclass. So at runtime, it can look at its own inheritance hierarchy, and figure out what type parameter you used forTypeToken
, and then returns ajava.lang.reflect.Type
instance for that type (which, if it is parameterized, will be aParameterizedType
instance). Once you get thisType
instance, you are supposed to pass it as the second argument of thetoGson()
.All we need to do is find another way to create this instance of
ParameterizedType
.ParameterizedType
is an interface, but unfortunately the public Java API does not provide any concrete implementations or any way to create aParameterizedType
dynamically. There appears to be a class calledParameterizedTypeImpl
, in the private Sun APIs and in the Gson code that you can use (e.g. here). You can simply copy the code and rename it into your own class. Then, to create aType
object representingFoo<String>
at runtime, you can just do something likenew ParameterizedTypeImpl(Foo.class, new Type[]{String.class}, null)
(untested)例如,假设我们正在讨论
List
。您可以构造如下内容:
如果您需要它来从 JSON 反序列化,则可以通过调用
gson.fromJson
使用此Type
Let's say we're talking about
List<String>
for example.You can construct something like this:
If you need this for de-serializing from JSON, you can use this
Type
from callinggson.fromJson
围绕类型擦除的通常方法是:
如果你的 T 没有无参数构造函数,你可以使用 Class 对象上的反射做一些更奇特的事情;一旦有了
Class
的实例,您就可以获得一个实例。我在使用 gson 时也遇到了这个问题。我最终得到了这样的结果:
当我需要反序列化时,我做了一个 Class.forName(className) ,然后我就拥有了调用 gson 库的 fromJson() 方法所需的一切。
我不敢相信 gson 本身不支持这个 - 这似乎是一件显而易见的事情......获取一些 json 并将其转换为一个对象而不事先知道它是哪个类。
The usual way around type erasure is:
If there's no no-args constructor for your T, you can do something fancier using reflection on the Class object; once you have an instance of
Class<T>
, you can get an instance.I faced exactly this problem too with gson. I ended up with this:
And when I needed to deserialise, I did a
Class.forName(className)
then I had all I needed to call the fromJson() method of the gson library.I couldn't believe gson did not support this natively - it seems like such an obvious thing to want to do... get some json and turn that into an object without knowing which class it is beforehand.
Gson 实际上为此提供了一个解决方案: https://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener
当然,它的作用只不过是Bohemian 的解决方案(您仍然需要以某种方式传递类型参数),但会自动为您完成。
Gson does actually provide a solution for this: https://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener
Of course what this does is little more than Bohemian's solution (you still need to pass the type parameter somehow), but done automatically for you.
其他人都这么说:)。您想要实例化的类需要在运行时可用。执行此操作的方法有很多:将类或类名放在工厂的本地变量中,使用受保护的方法,如果需要在许多不同的地方执行此操作,则创建一个“对象工厂”类,等等。 bean 框架所做的工作,因此如果您正在使用一个框架,则可以通过配置它来完成它。
What everyone else said :) . The class that you want to instantiate needs to be available at runtime. Many ways of doing this: put the class or class name in a variable local to your factory, have a protected method, create an "object factory" class if you need to do this in many different places, etc. This is the kind of job that bean frameworks do, so if you are using one, it might be possible to do it by configuring that.
它似乎比大多数人想象的要简单:
不需要像 newacct 建议的那样复制 ParameterizedTypeImpl 类。
It appeared to be really simpler than most people thought:
No need to copy ParameterizedTypeImpl class as newacct suggested.