Подтвердить что ты не робот

Передача класса с параметром типа в качестве параметра типа для общего метода в 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 будет использовать общий тип класса.

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

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

4b9b3361

Ответ 1

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

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

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

Проверьте класс TypeToken, который делает именно то, что вы хотите. Например:

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

Затем вы передаете TypeToken<T> вместо Class<T> и все. Он предоставляет вам методы для отражения в типе, представленном T.

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

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