如何在运行时从泛型类型定义和运行时类型参数构建 Java 类型对象?

发布于 2025-01-01 23:01:23 字数 156 浏览 0 评论 0原文

假设有一个泛型类型声明(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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

不美如何 2025-01-08 23:01:23

我想我明白你的问题。您想要序列化 ​​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 的类 (例如此处)。您只需复制代码并将其重命名为您自己的类即可。然后,要在运行时创建表示 FooType 对象,您只需执行 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 of T at runtime (but it's not fixed at compile time). Therefore, the suggested solution in Gson of creating an anonymous subclass of TypeToken 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 like Foo<T>.

However, let's look at what the TypeToken method on the Gson site actually accomplishes. You create an object of an anonymous subclass of TypeToken, and then ask for its type parameter using its getType() 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 for TypeToken, and then returns a java.lang.reflect.Type instance for that type (which, if it is parameterized, will be a ParameterizedType instance). Once you get this Type instance, you are supposed to pass it as the second argument of the toGson().

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 a ParameterizedType dynamically. There appears to be a class called ParameterizedTypeImpl, 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 a Type object representing Foo<String> at runtime, you can just do something like new ParameterizedTypeImpl(Foo.class, new Type[]{String.class}, null) (untested)

仅冇旳回忆 2025-01-08 23:01:23

例如,假设我们正在讨论 List
您可以构造如下内容:

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

            public Type getOwnerType() {
                return null;
            }

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

如果您需要它来从 JSON 反序列化,则可以通过调用 gson.fromJson 使用此 Type

Let's say we're talking about List<String> for example.
You can construct something like this:

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

            public Type getOwnerType() {
                return null;
            }

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

If you need this for de-serializing from JSON, you can use this Type from calling gson.fromJson

谁的年少不轻狂 2025-01-08 23:01:23

围绕类型擦除的通常方法是:

class Foo<T> {

    Class<T> clazz;

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

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

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

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

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

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

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

The usual way around type erasure is:

class Foo<T> {

    Class<T> clazz;

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

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

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:

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

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.

红衣飘飘貌似仙 2025-01-08 23:01:23

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.

水波映月 2025-01-08 23:01:23

其他人都这么说:)。您想要实例化的类需要在运行时可用。执行此操作的方法有很多:将类或类名放在工厂的本地变量中,使用受保护的方法,如果需要在许多不同的地方执行此操作,则创建一个“对象工厂”类,等等。 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.

对岸观火 2025-01-08 23:01:23

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

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

不需要像 newacct 建议的那样复制 ParameterizedTypeImpl 类。

It appeared to be really simpler than most people thought:

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

No need to copy ParameterizedTypeImpl class as newacct suggested.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文