Étant donné qu'il s'agit d'une question largement référencée et que les réponses actuelles expliquent principalement pourquoi cela ne fonctionne pas (ou proposent des solutions hacky et dangereuses que je n'aimerais jamais voir dans le code de production), je pense qu'il est approprié d'ajouter une autre réponse, montrant les pièges et une solution possible.
La raison pour laquelle cela ne fonctionne pas en général a déjà été signalée dans d'autres réponses: la validité ou non de la conversion dépend des types d'objets contenus dans la liste d'origine. Lorsqu'il y a des objets dans la liste dont le type n'est pas de type TestB
, mais d'une sous-classe différente de TestA
, la conversion n'est pas valide.
Bien sûr, les lancers peuvent être valides. Vous disposez parfois d'informations sur les types qui ne sont pas disponibles pour le compilateur. Dans ces cas, il est possible de lancer les listes, mais en général, il n'est pas recommandé :
On pourrait soit ...
- ... lancer toute la liste ou
- ... lancer tous les éléments de la liste
Les implications de la première approche (qui correspond à la réponse actuellement acceptée) sont subtiles. Cela peut sembler fonctionner correctement au premier coup d'œil. Mais s'il y a des types incorrects dans la liste d'entrée, alors un ClassCastException
sera jeté, peut-être à un emplacement complètement différent dans le code, et il peut être difficile de le déboguer et de découvrir où le mauvais élément s'est glissé dans la liste. Le pire problème est que quelqu'un pourrait même ajouter les éléments invalides après la conversion de la liste, ce qui rend le débogage encore plus difficile.
Le problème du débogage de ces parasites ClassCastExceptions
peut être atténué avec la Collections#checkedCollection
famille de méthodes.
Filtrage de la liste en fonction du type
Une façon plus sûre de convertir un de un List<Supertype>
en un List<Subtype>
consiste à filtrer la liste et à créer une nouvelle liste qui ne contient que des éléments qui ont un certain type. Il existe certains degrés de liberté pour la mise en œuvre d'une telle méthode (par exemple en ce qui concerne le traitement des null
entrées), mais une mise en œuvre possible peut ressembler à ceci:
/**
* 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;
}
Cette méthode peut être utilisée afin de filtrer des listes arbitraires (pas seulement avec une relation Subtype-Supertype donnée concernant les paramètres de type), comme dans cet exemple:
// 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]