J'ai un List<SubClass>
que je veux traiter comme un List<BaseClass>
. Il semble que cela ne devrait pas être un problème car la conversion de a SubClass
en a BaseClass
est un jeu d'enfant, mais mon compilateur se plaint que la distribution est impossible.
Alors, quel est le meilleur moyen d'obtenir une référence aux mêmes objets qu'un List<BaseClass>
?
En ce moment, je fais juste une nouvelle liste et je copie l'ancienne liste:
List<BaseClass> convertedList = new ArrayList<BaseClass>(listOfSubClass)
Mais si je comprends bien, cela doit créer une liste entièrement nouvelle. J'aimerais une référence à la liste originale, si possible!
java
inheritance
casting
Riley Lark
la source
la source
Réponses:
La syntaxe de ce type d'affectation utilise un caractère générique:
Il est important de comprendre que a
List<SubClass>
n'est pas interchangeable avec aList<BaseClass>
. Le code qui conserve une référence à l 'List<SubClass>
attendra que chaque élément de la liste soit unSubClass
. Si une autre partie du code se référait à la liste comme aList<BaseClass>
, le compilateur ne se plaindra pas lorsqu'unBaseClass
ouAnotherSubClass
est inséré. Mais cela entraînera unClassCastException
pour le premier morceau de code, ce qui suppose que tout dans la liste est unSubClass
.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 autorisé à faire ceci:
Ceci 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 labases
référence), une exception d'exécution sera levée.Les collections génériques ne «connaissent» pas leur type de composant; ces informations sont «effacées» au moment de la compilation. Par conséquent, ils ne peuvent pas déclencher d'exception d'exécution lorsqu'un magasin non valide se produit. Au lieu de cela, a
ClassCastException
sera déclenché à un point très éloigné et difficile à associer dans le code lorsqu'une valeur est lue à partir de 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.la source
erickson a déjà expliqué pourquoi vous ne pouvez pas faire cela, mais voici quelques solutions:
Si vous souhaitez uniquement retirer des éléments de votre liste de base, votre méthode de réception doit en principe être déclarée comme prenant un
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(...)
, ce qui permet de renvoyer une liste d'un supertype du paramètre de l'argument. (Cela évite le problème de sécurité de type en lançant une exception UnsupportedOperationException lors des tentatives d'insertion.)la source
Comme @erickson l'a expliqué, si vous voulez vraiment une référence à la liste d'origine, assurez-vous qu'aucun code n'insère quoi que ce soit dans cette liste si jamais vous souhaitez l'utiliser à nouveau sous sa déclaration d'origine. Le moyen le plus simple de l'obtenir est de simplement le convertir dans une vieille liste non générique:
Je ne recommanderais pas cela si vous ne savez pas ce qui arrive à la liste et vous suggérerais de modifier le code dont la liste a besoin pour accepter la liste que vous avez.
la source
Vous trouverez ci-dessous un extrait utile qui fonctionne. Il construit une nouvelle liste de tableaux, mais la création d'objets JVM au-dessus de la tête n'est pas significative.
J'ai vu que d'autres réponses n'étaient pas forcément compliquées.
la source
J'ai manqué la réponse où vous venez de lancer la liste originale, en utilisant une double distribution. Alors voici pour l'exhaustivité:
Rien n'est copié et l'opération est rapide. Cependant, vous trompez le compilateur ici, vous devez donc absolument vous assurer de ne pas modifier la liste de telle sorte que le
subList
début contienne 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.la source
List<BaseClass> convertedList = Collections.checkedList(listOfSubClass, BaseClass.class)
la source
Ce que vous essayez de faire est très utile et je trouve que je dois le faire très souvent dans le code que j'écris. Un exemple de cas d'utilisation:
Disons que nous avons une interface
Foo
et que nous avons unzorking
package qui a unZorkingFooManager
qui crée et gère des instances de package-privateZorkingFoo implements Foo
. (Un scénario très courant.)Donc,
ZorkingFooManager
doit contenir unprivate Collection<ZorkingFoo> zorkingFoos
mais il doit exposer un fichierpublic Collection<Foo> getAllFoos()
.La plupart des programmeurs java ne réfléchiraient pas à deux fois avant de l'implémenter
getAllFoos()
en allouant un nouveauArrayList<Foo>
, en le remplissant avec tous les élémentszorkingFoos
et en le renvoyant. J'apprécie l'idée qu'environ 30% de tous les cycles d'horloge consommés par le code java s'exécutant sur des millions de machines partout dans le monde ne font rien d'autre que créer des copies inutiles de ArrayLists qui sont récupérées en microsecondes après leur création.La solution à ce problème est, bien entendu, de réduire la collection. Voici la meilleure façon de le faire:
Ce qui nous amène à la
castList()
fonction:La
result
variable intermédiaire est nécessaire en raison d'une perversion du langage java:return (List<T>)list;
produit une exception "cast non vérifié"; jusqu'ici tout va bien; mais alors:@SuppressWarnings( "unchecked" ) return (List<T>)list;
est une utilisation illégale de l'annotation suppress-warnings.Ainsi, même s'il n'est pas casher à utiliser
@SuppressWarnings
sur unereturn
instruction, il est apparemment bien de l'utiliser sur une affectation, donc la variable supplémentaire "result" résout ce problème. (Il devrait être optimisé par le compilateur ou par le JIT de toute façon.)la source
Quelque chose comme ça devrait fonctionner aussi:
Je ne sais pas vraiment pourquoi Clazz est nécessaire dans Eclipse.
la source
Que diriez-vous de lancer tous les éléments. Il créera une nouvelle liste, mais référencera les objets d'origine de l'ancienne liste.
la source
C'est le morceau de code de travail complet utilisant les génériques, pour convertir la liste de sous-classes en super classe.
Méthode de l'appelant qui passe le type de sous-classe
Méthode de l'appelé qui accepte n'importe quel sous-type de la classe de base
la source