Передача класса с параметром типа в качестве параметра типа для универсального метода в Java


предупреждение: Я хотел бы передать класс с параметром типа (например,ArrayList<SomeClass>, например) к универсальному методу в качестве параметра типа.

допустим, у меня есть метод:

    public static <T> T getGenericObjectFromJson(String json, Class<T> genericType){
        // details unimportant, basically returns an object of specified type
        return JsonParser.fromJson(json, genericType); 
    }

этот метод, конечно, будет отлично работать и для любого класса. Я могу вызвать метод так, например:

getGenericObjectFromJson(jsonString, User.class)

проблема: я обнаружил, что не могу этого сделать:

getGenericObjectFromJson(jsonString, ArrayList<User>.class)

синтаксически, это очевидно, недействительным. Однако я не уверен, как бы я даже сделал что-то подобное. Я могу, конечно, пройти ArrayList.class, однако добавление универсального типа больше не делает его синтаксически допустимым, и я не могу придумать способ обойти его.

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

getGenericObjectFromJson(jsonString, new ArrayList<User>().getClass())

однако мы все равно теряем общий тип и просто возвращаем ArrayList неизвестного типа (хотя это может быть отбрасывать.) Кроме того, излишне создавать экземпляр объекта.

мое единственное решение было обернуть этот метод в класс, который содержит параметр универсального типа, который может быть создан, например, так:
public class JsonDeserializer<T>...

в этом случае getGenericObjectFromJson метод будет использовать универсальный тип класса.

вопрос(ы): в конечном счете, мне любопытно, почему я не могу передать класс с параметром типа, и есть ли способ выполнить то, что я пытался делать.

как всегда, дайте мне знать, если есть какие-то проблемы с этим вопросом.

1   51   2013-08-27 21:14:32

1 ответ:

это на самом деле возможно в Java, используя некоторые "трюки". Не поддавайтесь давлению со стороны фанатиков C#! (j / k)

"трюк" состоит в том, чтобы создать класс, который расширяет универсальный тип, и получить доступ к значению параметра типа родительского класса через Type возвращено .getGenericSuperclass() или .getGenericInterfaces().

это довольно громоздко. Чтобы упростить нашу жизнь, Google уже написал большую часть скучной части кода для нас и сделал его доступным через Гуава.

Регистрация TypeToken класс, который делает именно то, что вы хотите. Например:

TypeToken<List<String>> stringListTok = new TypeToken<List<String>>() {};

затем вы проходите вокруг TypeToken<T> вместо Class<T> и это все. Он предоставляет вам методы для отражения типа, представленного T.

то, что это делает внутренне, просто вызывает .getClass().getGenericSuperclass() (или ...Interfaces()), то некоторые уродливые слепки с Type to ParameterizedType и получение всей информации оттуда (.getActualTypeArguments(), etc).

наконец, если вы хотите сделать что-то подобное с инъекцией зависимостей (т. е. предположим, что вам нужно ввести Class<T> на конструкторе класса, или вы хотите получить экземпляр некоторого параметризованного интерфейса, в котором экземпляр должен зависеть от параметра типа), Google Guice (контейнер DI от Google) имеет очень похожий механизм для решения проблемы, называемый TypeLiteral. Использование и код за кулисами почти идентичны TypeToken от Гуава. Проверьте это здесь:TypeLiteral