Como essa é uma pergunta amplamente referenciada e as respostas atuais explicam principalmente por que ela não funciona (ou propõe soluções perigosas e hacky que eu nunca gostaria de ver no código de produção), acho apropriado adicionar outra resposta, mostrando as armadilhas e uma possível solução.
A razão pela qual isso não funciona em geral já foi apontada em outras respostas: A validade ou não da conversão depende dos tipos de objetos contidos na lista original. Quando houver objetos na lista cujo tipo não seja do tipo TestB
, mas de uma subclasse diferente de TestA
, a conversão não será válida.
Obviamente, os lançamentos podem ser válidos. Às vezes, você tem informações sobre os tipos que não estão disponíveis para o compilador. Nesses casos, é possível lançar as listas, mas, em geral, não é recomendado :
Pode-se também ...
- ... lançar a lista inteira ou
- ... lança todos os elementos da lista
As implicações da primeira abordagem (que corresponde à resposta atualmente aceita) são sutis. Pode parecer funcionar corretamente à primeira vista. Mas se houver tipos errados na lista de entrada, a ClassCastException
será lançada, talvez em um local completamente diferente no código, e pode ser difícil depurar isso e descobrir onde o elemento errado entrou na lista. O pior problema é que alguém pode até adicionar os elementos inválidos após a lista ser lançada, tornando a depuração ainda mais difícil.
O problema de depurar esses espúrios ClassCastExceptions
pode ser aliviado com a Collections#checkedCollection
família de métodos.
Filtrando a lista com base no tipo
Uma maneira mais segura de se converter de um List<Supertype>
para um List<Subtype>
é filtrar a lista e criar uma nova lista que contenha apenas elementos que tenham determinado tipo. Existem alguns graus de liberdade para a implementação de um método desse tipo (por exemplo, com relação ao tratamento de null
entradas), mas uma possível implementação pode ser assim:
/**
* Filter the given list, and create a new list that only contains
* the elements that are (subtypes) of the class c
*
* @param listA The input list
* @param c The class to filter for
* @return The filtered list
*/
private static <T> List<T> filter(List<?> listA, Class<T> c)
{
List<T> listB = new ArrayList<T>();
for (Object a : listA)
{
if (c.isInstance(a))
{
listB.add(c.cast(a));
}
}
return listB;
}
Este método pode ser usado para filtrar listas arbitrárias (não apenas com um determinado relacionamento Subtype-Supertype em relação aos parâmetros de tipo), como neste exemplo:
// A list of type "List<Number>" that actually
// contains Integer, Double and Float values
List<Number> mixedNumbers =
new ArrayList<Number>(Arrays.asList(12, 3.4, 5.6f, 78));
// Filter the list, and create a list that contains
// only the Integer values:
List<Integer> integers = filter(mixedNumbers, Integer.class);
System.out.println(integers); // Prints [12, 78]