157 votes

Le moyen le plus efficace de convertir une liste de sous-classes en une liste de classes de base.

J'ai un List<SubClass> que je veux traiter comme un List<BaseClass> . Il semble que cela ne devrait pas être un problème puisque le fait de lancer un SubClass à un BaseClass est un jeu d'enfant, mais mon compilateur se plaint que le cast est impossible.

Alors, quelle est la meilleure façon d'obtenir une référence aux mêmes objets qu'une List<BaseClass> ?

Pour l'instant, je fais juste une nouvelle liste et je copie l'ancienne :

List<BaseClass> convertedList = new ArrayList<BaseClass>(listOfSubClass)

Mais si je comprends bien, il faut créer une liste entièrement nouvelle. J'aimerais avoir une référence à la liste originale, si possible !

3 votes

La réponse que vous avez ici : stackoverflow.com/questions/662508/

203voto

erickson Points 127945

La syntaxe pour ce type d'affectation utilise un caractère de remplacement :

List<SubClass> subs = ...;
List<? extends BaseClass> bases = subs;

Il est important de comprendre qu'un List<SubClass> est pas interchangeable avec un List<BaseClass> . Code qui conserve une référence à la List<SubClass> s'attendra à ce que chaque élément de la liste soit un fichier de type SubClass . Si une autre partie du code se réfère à la liste en tant que List<BaseClass> le compilateur ne se plaindra pas lorsqu'un BaseClass ou AnotherSubClass est inséré. Mais cela provoquera un ClassCastException pour le premier morceau de code, qui suppose que tout ce qui se trouve dans la liste est une SubClass .

Les collections génériques ne se comportent pas de la même manière que les tableaux en Java. Les tableaux sont covariants, c'est-à-dire qu'il est permis de faire cela :

SubClass[] subs = ...;
BaseClass[] bases = subs;

Cela est autorisé, car le tableau "connaît" le type de ses éléments. Si quelqu'un tente de stocker quelque chose qui n'est pas une instance de SubClass dans le tableau (via la fonction bases ), une exception d'exécution sera déclenchée.

Les collections génériques font pas "connaissent" leur type de composant ; cette information est "effacée" au moment de la compilation. Par conséquent, ils ne peuvent pas lever une exception d'exécution lorsqu'un magasin invalide se produit. Au lieu de cela, un ClassCastException sera soulevée à un moment éloigné et difficile à associer dans le code lorsqu'une valeur est lue dans la collection. Si vous tenez compte des avertissements du compilateur concernant la sécurité des types, vous éviterez ces erreurs de type au moment de l'exécution.

0 votes

Il convient de noter qu'avec les tableaux, au lieu d'une ClassCastException lors de la récupération d'un objet qui n'est pas de type SubClass (ou dérivé), vous obtiendrez une ArrayStoreException lors de l'insertion.

0 votes

Merci surtout pour l'explication de la raison pour laquelle les deux listes ne peuvent être considérées comme identiques.

0 votes

Le système de types Java prétend que les tableaux sont covariants, mais ils ne sont pas vraiment substituables, comme le prouve l'exception ArrayStoreException.

44voto

Paŭlo Ebermann Points 35526

Erickson a déjà expliqué pourquoi vous ne pouvez pas le faire, mais voici quelques solutions :

Si vous souhaitez uniquement retirer des éléments de votre liste de base, votre méthode de réception devrait en principe être déclarée comme prenant un fichier List<? extends BaseClass> .

Mais si ce n'est pas le cas et que vous ne pouvez pas le changer, vous pouvez envelopper la liste avec Collections.unmodifiableList(...) qui permet de retourner une liste d'un supertype du paramètre de l'argument. (Elle évite le problème de sécurité de type en lançant UnsupportedOperationException lors des tentatives d'insertion).

0 votes

Super ! Je ne connaissais pas celle-là.

14voto

hd42 Points 514

Comme l'a expliqué @erickson, si vous voulez vraiment une référence à la liste originale, assurez-vous qu'aucun code n'insère quoi que ce soit dans cette liste si vous voulez un jour l'utiliser à nouveau sous sa déclaration originale. La façon la plus simple de l'obtenir est de la transformer en une vieille liste non générique :

List<BaseClass> baseList = (List)new ArrayList<SubClass>();

Je ne le recommande pas si vous ne savez pas ce qu'il advient de la liste et je vous suggère de modifier le code qui a besoin de la liste pour accepter la liste que vous avez.

6voto

john16384 Points 118

J'ai raté la réponse où il suffit de lancer la liste originale, en utilisant un double lancer. La voici donc, par souci d'exhaustivité :

List<BaseClass> baseList = (List<BaseClass>)(List<?>)subList;

Rien n'est copié, et l'opération est rapide. Cependant, vous trompez le compilateur ici et vous devez donc être absolument sûr de ne pas modifier la liste de manière à ce que la fonction subList commence à contenir des éléments d'un sous-type différent. Lorsqu'il s'agit de listes immuables, ce n'est généralement pas un problème.

3voto

sigirisetti Points 179

Vous trouverez ci-dessous un extrait utile qui fonctionne. Il construit une nouvelle liste de tableaux mais la création d'objets JVM est insignifiante.

J'ai vu que d'autres réponses sont inutilement compliquées.

List<BaseClass> baselist = new ArrayList<>(sublist);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X