简体   繁体   English

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

[英]How do I build a Java type object at runtime from a generic type definition and runtime type parameters?

Assuming a generic type declaration (Java) 假设泛型类型声明(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)? 我怎样才能在运行时实例化一个Type对象,该对象表示在特定类型T上参数化的Foo(也仅在运行时知道)?

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). 您希望序列化Foo<T> ,并且在运行时具有T的类对象(但在编译时它并未修复)。 Therefore, the suggested solution in Gson of creating an anonymous subclass of TypeToken does not work because that requires that the parameterized type (eg Foo<String> ) be hard-coded at compile time, and it does not work if you use something like Foo<T> . 因此,在Gson中建议的创建TypeToken的匿名子类的解决方案不起作用,因为这要求参数化类型(例如Foo<String> )在编译时被硬编码,并且如果您使用类似Foo<T>东西它不起作用Foo<T>

However, let's look at what the TypeToken method on the Gson site actually accomplishes. 但是,让我们看一下Gson站点上的TypeToken方法实际完成了什么。 You create an object of an anonymous subclass of TypeToken , and then ask for its type parameter using its getType() method. 您创建一个TypeToken的匿名子类的TypeToken ,然后使用其getType()方法请求其类型参数。 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). 因此,在运行时,它可以查看自己的继承层次结构,并找出用于TypeToken类型参数,然后返回该类型的java.lang.reflect.Type实例(如果参数化,将为ParameterizedType实例)。 Once you get this Type instance, you are supposed to pass it as the second argument of the toGson() . 获得此Type实例后,您应该将其作为toGson()的第二个参数传递。

All we need to do is find another way to create this instance of ParameterizedType . 我们需要做的就是找到另一种方法来创建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. ParameterizedType是一个接口,但遗憾的是,公共Java API不提供任何具体实现或任何方式来动态创建ParameterizedType There appears to be a class called ParameterizedTypeImpl , in the private Sun APIs and in the Gson code that you can use ( eg here ). 似乎有一个名为ParameterizedTypeImpl的类,在私有Sun API和您可以使用的Gson代码中( 例如此处 )。 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) 然后,要在运行时创建表示Foo<String>Type对象,您可以执行类似new ParameterizedTypeImpl(Foo.class, new Type[]{String.class}, null) (未经测试)

Let's say we're talking about List<String> for example. 假设我们正在谈论List<String>
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 如果您需要这个来从JSON反序列化,您可以使用此Type来调用gson.fromJson

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; 如果你的T没有no-args构造函数,你可以使用Class对象的反射来做更好的事情; once you have an instance of Class<T> , you can get an instance. 一旦你有一个Class<T>的实例,你就可以获得一个实例。

I faced exactly this problem too with gson. 我用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. 当我需要反序列化时,我做了一个Class.forName(className)然后我需要调用gson库的fromJson()方法。

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本身不支持这一点 - 想要做的事情似乎是一件显而易见的事情......得到一些json并将其变成一个对象而不知道它是哪个类。

Gson does actually provide a solution for this: https://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener Gson确实为此提供了解决方案: 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. 当然这只是波希米亚语的解决方案(你仍需要以某种方式传递类型参数),但是会自动完成。

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. 许多方法:将类或类名放在工厂的本地变量中,使用受保护的方法,如果需要在许多不同的地方执行此操作,则创建“对象工厂”类等。这是一种bean框架所做的工作,所以如果你正在使用它,可以通过配置它来实现。

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. 无需将ParameterizedTypeImpl类复制为newacct建议。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM