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

时间:2012-02-02 11:57:03

标签: java generics reflection

假设泛型类型声明(Java)

class Foo<T> {
    public T bar;
}

我怎样才能在运行时实例化一个Type对象,该对象表示通过特定类型T参数化的Foo(也仅在运行时知道)?

6 个答案:

答案 0 :(得分:13)

我想我理解你的问题。您希望序列化Foo<T>,并且在运行时具有T的类对象(但在编译时它没有修复)。因此,在Gson中建议的创建TypeToken的匿名子类的解决方案不起作用,因为这要求参数化类型(例如Foo<String>)在编译时被硬编码,并且如果你使用像Foo<T>这样的东西。

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

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

之类的操作

答案 1 :(得分:4)

我们假设我们正在谈论List<String>
你可以构建这样的东西:

    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

答案 2 :(得分:2)

关于类型擦除的常用方法是:

class Foo<T> {

    Class<T> clazz;

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

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

如果你的T没有no-args构造函数,你可以使用Class对象的反射来做更好的事情;一旦你有一个Class<T>的实例,你就可以获得一个实例。

我和gson也面对这个问题。我最终得到了这个:

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

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

我简直不敢相信gson本身不支持这一点 - 想要这样做是显而易见的事情......得到一些json并将其变成一个对象而不用知道哪个类它是事先。

答案 3 :(得分:-1)

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

当然这只是波希米亚的解决方案(你还需要以某种方式传递类型参数),但是会自动完成。

答案 4 :(得分:-1)

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

答案 5 :(得分:-2)

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

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

无需将ParameterizedTypeImpl类复制为newacct建议。