Par exemple, supposons que vous ayez deux classes:
public class TestA {}
public class TestB extends TestA{}
J'ai une méthode qui retourne un List<TestA>
et je voudrais convertir tous les objets de cette liste pour TestB
que je me retrouve avec un List<TestB>
.
la conversion de génériques n'est pas possible, mais si vous définissez la liste d'une autre manière, il est possible d'y stocker TestB:
Vous devez toujours vérifier le type lorsque vous utilisez les objets de la liste.
la source
Cannot instantiate the type ArrayList<? extends TestA>
.Vous ne pouvez vraiment pas *:
Un exemple est tiré de ce tutoriel Java
Supposons qu'il existe deux types
A
etB
tels queB extends A
. Ensuite, le code suivant est correct:Le code précédent est valide car il
B
s'agit d'une sous-classe deA
. Maintenant, que se passe-t-il avecList<A>
etList<B>
?Il s'avère que ce
List<B>
n'est pas une sous-classe deList<A>
donc nous ne pouvons pas écrireDe plus, nous ne pouvons même pas écrire
*: Pour rendre le casting possible, nous avons besoin d'un parent commun pour les deux
List<A>
etList<B>
:List<?>
par exemple. Ce qui suit est valide:Vous recevrez cependant un avertissement. Vous pouvez le supprimer en ajoutant
@SuppressWarnings("unchecked")
à votre méthode.la source
Avec Java 8, vous pouvez réellement
la source
new List<TestB>
, une boucle et un casting?Je pense que vous lancez dans la mauvaise direction cependant ... si la méthode retourne une liste d'
TestA
objets, alors il n'est vraiment pas sûr de les convertir enTestB
.Fondamentalement, vous demandez au compilateur de vous permettre d'effectuer des
TestB
opérations sur un typeTestA
qui ne les prend pas en charge.la source
É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 deTestA
, 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 ...
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 laCollections#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 unList<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 desnull
entrées), mais une mise en œuvre possible peut ressembler à ceci: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:
la source
Vous ne pouvez pas lancer
List<TestB>
àList<TestA>
comme Steve Kuo mentionne mais vous pouvez vider le contenu deList<TestA>
dansList<TestB>
. Essayez ce qui suit:Je n'ai pas essayé ce code donc il y a probablement des erreurs mais l'idée est qu'il devrait itérer à travers l'objet de données en ajoutant les éléments (objets TestB) dans la liste. J'espère que cela fonctionne pour vous.
la source
Le meilleur moyen sûr consiste à implémenter un
AbstractList
élément et à le convertir en implémentation. J'ai créé uneListUtil
classe d'assistance:Vous pouvez utiliser une
cast
méthode pour lancer aveuglément des objets dans la liste et uneconvert
méthode pour une diffusion en toute sécurité. Exemple:la source
La seule façon que je connaisse est de copier:
la source
Lorsque vous convertissez une référence d'objet, vous transformez simplement le type de la référence, pas le type de l'objet. le casting ne changera pas le type réel de l'objet.
Java n'a pas de règles implicites pour convertir les types d'objets. (Contrairement aux primitives)
Au lieu de cela, vous devez indiquer comment convertir un type en un autre et l'appeler manuellement.
C'est plus verbeux que dans une langue avec un support direct, mais cela fonctionne et vous ne devriez pas avoir besoin de le faire très souvent.
la source
si vous avez un objet de la classe
TestA
, vous ne pouvez pas le convertirTestB
. toutTestB
est unTestA
, mais pas l'inverse.dans le code suivant:
la deuxième ligne lancerait un
ClassCastException
.vous ne pouvez convertir une
TestA
référence que si l'objet lui-même l'estTestB
. par exemple:ainsi, vous ne pouvez pas toujours lancer une liste de
TestA
dans une liste deTestB
.la source
Vous pouvez utiliser la
selectInstances
méthode dans les collections Eclipse . Cela impliquera la création d'une nouvelle collection mais ne sera donc pas aussi efficace que la solution acceptée qui utilise le moulage.J'ai inclus
StringBuffer
dans l'exemple pour montrer queselectInstances
non seulement rétrograde le type, mais filtrera également si la collection contient des types mixtes.Remarque: je suis un committer pour les collections Eclipse.
la source
Cela est possible en raison de l'effacement du type. Vous constaterez que
En interne, les deux listes sont de type
List<Object>
. Pour cette raison, vous ne pouvez pas lancer l'un sur l'autre - il n'y a rien à lancer.la source
Integer j = 42; Integer i = (Integer) j;
fonctionne très bien, les deux sont des entiers, ils ont tous les deux la même classe, donc "il n'y a rien à lancer".Le problème est que votre méthode ne retourne PAS une liste de TestA si elle contient un TestB, alors que faire si elle a été correctement tapée? Ensuite, ce casting:
fonctionne aussi bien que vous pouvez l'espérer (Eclipse vous avertit d'une distribution non contrôlée qui est exactement ce que vous faites, alors meh). Pouvez-vous donc l'utiliser pour résoudre votre problème? En fait, vous pouvez à cause de cela:
Exactement ce que vous avez demandé, non? ou pour être vraiment exact:
semble compiler très bien pour moi.
la source
Ce test montre comment caster
List<Object>
versList<MyClass>
. Mais vous devez faire attention à ce qu'ilobjectList
doit contenir des instances du même type queMyClass
. Et cet exemple peut être pris en compte lorsqu'ilList<T>
est utilisé. Pour cela, récupérez le champClass<T> clazz
dans le constructeur et utilisez-le à la place deMyClass.class
.la source
Cela devrait fonctionner
la source
Il est assez étrange que la conversion manuelle d'une liste ne soit toujours pas fournie par une boîte à outils implémentant quelque chose comme:
Bien sûr, cela ne vérifiera pas les éléments un par un, mais c'est précisément ce que nous voulons éviter ici, si nous savons bien que notre implémentation ne fournit que le sous-type.
la source