Como obter instância de classe parametrizada
Pergunta
Desde que os genéricos foram introduzidos, Class é parametrizado, de modo que List.class produz Class<List>.Isto está claro.
O que não consigo descobrir é como obter uma instância de classe do tipo que é parametrizada, ou seja,Classe<Lista<String>>.Como neste trecho:
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);
}
}
Eu me deparo com variações desse problema com bastante frequência e ainda não sei se estou perdendo alguma coisa ou se realmente não há maneira melhor.Obrigado por sugestões.
Solução
A classe Class é uma representação em tempo de execução de um tipo.Como os tipos parametrizados passam por apagamento de tipo em tempo de execução, o objeto de classe para Class seria o mesmo que para Class<List<Integer>> e Class<List<String>>.
A razão pela qual você não pode instanciá-los usando a notação .class é que esta é uma sintaxe especial usada para literais de classe.O Especificação da linguagem Java proíbe especificamente essa sintaxe quando o tipo é parametrizado, e é por isso que List<String>.class não é permitido.
Outras dicas
As classes representam classes carregadas por um carregador de classes, que são tipos brutos.Para representar um tipo parametrizado, use java.lang.reflect.ParameterizedType.
Eu não acho que você possa fazer o que está tentando.Em primeiro lugar, seu método de instanciação não sabe que está lidando com um tipo parametrizado (você poderia facilmente passá-lo java.util.Date.class).Em segundo lugar, devido ao apagamento, é difícil ou impossível fazer algo particularmente específico com tipos parametrizados em tempo de execução.
Se você abordar o problema de uma maneira diferente, existem outros pequenos truques que você pode fazer, como inferência de tipo:
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();
}
}
A única coisa que você pode fazer é instanciar List<String>
diretamente e chame seu getClass()
:
instantiate(new List<String>() { ... }.getClass());
Para tipos com vários métodos abstratos como List
, isso é bastante estranho.Mas, infelizmente, chamar construtores de subclasses (como new ArrayList<String>
) ou métodos de fábrica (Collections.<String>emptyList()
) não funciona.