Как получить параметризованный экземпляр класса
Вопрос
Поскольку были введены дженерики, класс параметризован, так что List.class создает класс<List>.Это и так понятно.
Чего я не могу понять, так это как получить экземпляр класса типа, который сам параметризуется, т.е.Класс<List<String>>.Как в этом фрагменте:
public class GenTest {
static <T> T instantiate(Class<T> clazz) throws Exception {
return clazz.newInstance();
}
public static void main(String[] args) throws Exception {
// Is there a way to avoid waring on the line below
// without using @SuppressWarnings("unchecked")?
// ArrayList.class is Class<ArrayList>, but I would like to
// pass in Class<ArrayList<String>>
ArrayList<String> l = GenTest.instantiate(ArrayList.class);
}
}
Я довольно часто сталкиваюсь с вариациями этой проблемы и до сих пор не знаю, то ли я просто что-то упускаю, то ли действительно нет лучшего способа.Спасибо за предложения.
Решение
Класс class - это представление типа во время выполнения.Поскольку параметризованные типы подвергаются стиранию типа во время выполнения, объект class для Class будет таким же, как для Class<List<Integer>> и Class<List<String>>.
Причина, по которой вы не можете создать их экземпляр, используя обозначение .class, заключается в том, что это специальный синтаксис, используемый для литералов класса.Тот Самый Спецификация языка Java специально запрещает этот синтаксис при параметризации типа, вот почему List<String>.class не разрешен.
Другие советы
Классы представляют собой классы, загружаемые загрузчиком классов, которые являются необработанными типами.Чтобы представить параметризованный тип, используйте java.lang.reflect.ParameterizedType.
Я не думаю, что ты сможешь сделать то, что пытаешься.Во-первых, ваш метод instantiate не знает, что он имеет дело с параметризованным типом (вы могли бы так же легко передать его java.util.Date.class).Во-вторых, из-за стирания выполнение чего-либо особенно специфичного с параметризованными типами во время выполнения затруднено или невозможно.
Если бы вы подошли к проблеме по-другому, есть другие маленькие хитрости, которые вы можете сделать, например, вывод типа:
public class GenTest
{
private static <E> List<E> createList()
{
return new ArrayList<E>();
}
public static void main(String[] args)
{
List<String> list = createList();
List<Integer> list2 = createList();
}
}
Единственное, что вы можете сделать, это создать экземпляр List<String>
непосредственно и назовите ее getClass()
:
instantiate(new List<String>() { ... }.getClass());
Для типов с несколькими абстрактными методами, такими как List
, это довольно неловко.Но, к сожалению, вызов конструкторов подкласса (например new ArrayList<String>
) или заводские методы (Collections.<String>emptyList()
) не работай.