Как построить объект типа Java во время выполнения из определения общего типа и параметров типа времени выполнения? - PullRequest
9 голосов
/ 02 февраля 2012

Принимая объявление универсального типа (Java)

class Foo<T> {
    public T bar;
}

как я могу во время выполнения создать экземпляр объекта Type, представляющего Foo, параметризованного для определенного типа T (также известного только во время выполнения)?

Ответы [ 6 ]

13 голосов
/ 03 февраля 2012

Мне кажется, я понимаю ваш вопрос.Вы хотите сериализовать Foo<T>, и у вас есть объект класса T во время выполнения (но это не исправлено во время компиляции).Поэтому предлагаемое в Gson решение по созданию анонимного подкласса TypeToken не работает, поскольку для этого необходимо, чтобы параметризованный тип (например, Foo<String>) был жестко запрограммирован во время компиляции, и он не работает, если вы используете что-то вродеFoo<T>.

Однако давайте посмотрим, что на самом деле выполняет метод TypeToken на сайте Gson.Вы создаете объект анонимного подкласса TypeToken, а затем запрашиваете его параметр типа, используя его метод getType().Суперкласс класса является частью его метаданных и включает в себя общие параметры своего суперкласса.Таким образом, во время выполнения он может посмотреть собственную иерархию наследования и выяснить, какой параметр типа вы использовали для TypeToken, а затем вернуть экземпляр java.lang.reflect.Type для этого типа (который, если он параметризован, будет * 1013).* пример).Получив этот экземпляр Type, вы должны передать его как второй аргумент toGson().

Все, что нам нужно сделать, - это найти другой способ создания этого экземпляра ParameterizedType.ParameterizedType - это интерфейс, но, к сожалению, общедоступный API Java не предоставляет никаких конкретных реализаций или какого-либо способа динамического создания ParameterizedType.Похоже, есть класс с именем ParameterizedTypeImpl в частных API Sun и в коде Gson, который вы можете использовать (, например, здесь ).Вы можете просто скопировать код и переименовать его в свой собственный класс.Затем, чтобы создать Type объект, представляющий Foo<String> во время выполнения, вы можете просто сделать что-то вроде new ParameterizedTypeImpl(Foo.class, new Type[]{String.class}, null) (не проверено)

4 голосов
/ 10 августа 2014

Допустим, мы говорим о 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 голосов
/ 02 февраля 2012

Обычный способ удаления типа:

class Foo<T> {

    Class<T> clazz;

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

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

Если для вашего T нет конструктора без аргументов, вы можете сделать что-нибудь более изобразительное, используя отражение в объекте Class;когда у вас есть экземпляр Class<T>, вы можете получить экземпляр.

Я тоже столкнулся с этой проблемой с gson.Я закончил с этим:

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

И когда мне нужно было десериализовать, я сделал Class.forName(className), тогда у меня было все необходимое для вызова метода fromJson () библиотеки gson.

Я не мог поверить, что gson не поддерживал это изначально - кажется очевидной вещью, которую нужно сделать ... получить json и превратить его в объект без , заранее зная, к какому классу это относится,

0 голосов
/ 06 февраля 2013

Это оказалось действительно проще, чем думали многие:

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

Нет необходимости копировать класс ParameterizedTypeImpl в соответствии с предложением newacct.

0 голосов
/ 03 февраля 2012

То, что все остальные сказали :). Класс, который вы хотите создать, должен быть доступен во время выполнения. Есть много способов сделать это: поместить класс или имя класса в локальную переменную вашей фабрики, иметь защищенный метод, создать класс «фабрики объектов», если вам нужно сделать это в разных местах и ​​т. Д. работа, которую выполняют bean-фреймворки, поэтому, если вы используете ее, возможно, это можно сделать, настроив ее.

0 голосов
/ 02 февраля 2012

Gson действительно предлагает решение для этого: https://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener

Конечно, это немного больше, чем решение Богемиана (вам все равно нужно как-то передать параметр типа), но для вас это делается автоматически.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...