Comment convertir List <Object> en List <MyClass>


Réponses:


156

vous pouvez toujours convertir n'importe quel objet en n'importe quel type en le convertissant d'abord en Object. dans ton cas:

(List<Customer>)(Object)list; 

vous devez être sûr qu'au moment de l'exécution, la liste ne contient que des objets Customer.

Les critiques disent qu'un tel casting indique quelque chose qui ne va pas avec votre code; vous devriez pouvoir modifier vos déclarations de type pour éviter cela. Mais les génériques Java sont trop compliqués et pas parfaits. Parfois, vous ne savez tout simplement pas s'il existe une jolie solution pour satisfaire le compilateur, même si vous connaissez très bien les types d'exécution et que vous savez que ce que vous essayez de faire est sûr. Dans ce cas, faites simplement le moulage brut au besoin, afin de pouvoir quitter le travail pour la maison.


8
Cela a également besoin @SuppressWarnings("unchecked"). Notez que vous pouvez également effectuer une conversion ascendante vers (List)au lieu de vers (Object).
200_success

36

En effet, bien qu'un client soit un objet, une liste de clients n'est pas une liste d'objets. Si c'était le cas, vous pourriez mettre n'importe quel objet dans une liste de clients.


2
Non, ça ne l'est pas. Vous contourneriez la sécurité de type si ce qui précède était autorisé. Notez que la diffusion ne signifie pas créer une nouvelle liste et copier les éléments. Cela signifie gérer l'instance unique comme un type différent, et ainsi vous auriez une liste qui contient des objets potentiellement non clients avec une garantie de sécurité de type qu'elle ne devrait pas. Cela n'a rien à voir avec Java en tant que tel. Puisque vous pensez que cette fonctionnalité laisse Java coincé à l'âge sombre, je vous mets au défi d'expliquer comment vous pensez que cela devrait fonctionner à la place.
Lasse V.Karlsen

1
@ BrainSlugs83 pour votre besoin (List <Customer>) (Object) list;
Muthu Ganapathy Nathan

@ LasseV.Karlsen Je ne parle pas de casting, je parle de conversion. Cela ne contournerait pas la sécurité du type, il la ferait respecter. L'implémentation par Java de génériques, le manque de surcharges d'opérateurs, de méthodes d'extension et de nombreux autres équipements modernes m'ont laissé épiquement déçu. - Pendant ce temps, .NET a deux méthodes d'extension distinctes pour cela - une appelée .Cast<T>()et une appelée .OfType<T>(). Le premier effectue un cast sur chaque élément (en lançant les exceptions souhaitées) tandis que le second filtre les éléments qui ne peuvent pas être castés (vous en choisiriez donc un en fonction de votre scénario d'utilisation).
BrainSlugs83

1
@EAGER_STUDENT Je ne mettrais pas au-delà de Java que cela pourrait réellement fonctionner (toute cette affaire d'effacement de type ridicule, je devrais l'essayer ...) - mais non, je n'écrirais jamais de code comme ça - vous perdez type safety, que se passe-t-il si un élément de la collection n'est pas un instanceofclient?
BrainSlugs83

1
@ BrainSlugs83 La personne qui a posé la question a spécifiquement posé des questions sur le casting. Et si Java ne dispose pas déjà des méthodes pertinentes comme les méthodes .NET auxquelles vous faites référence (qui ne convertissent toujours pas btw), vous devriez pouvoir les ajouter facilement. Tout cela est un peu orthogonal à la question en cours, qui portait sur une syntaxe spécifique.
Lasse V. Karlsen

35

En fonction de votre autre code, la meilleure réponse peut varier. Essayer:

List<? extends Object> list = getList();
return (List<Customer>) list;

ou

List list = getList();
return (List<Customer>) list;

Mais gardez à l'esprit qu'il n'est pas recommandé de faire de tels lancers non contrôlés.


3
-1 - c'est une très mauvaise habitude à prendre, et à moins que vous n'utilisiez l'annotation "Unchecked", le compilateur s'en plaindra toujours
kdgregory

24

Avec Java 8 Streams :

Parfois, le casting par force brute convient:

List<MyClass> mythings = (List<MyClass>) (Object) objects

Mais voici une solution plus polyvalente:

List<Object> objects = Arrays.asList("String1", "String2");

List<String> strings = objects.stream()
                       .map(element->(String) element)
                       .collect(Collectors.toList());

Il y a une tonne d'avantages, mais l'un est que vous pouvez diffuser votre liste plus élégamment si vous ne pouvez pas être sûr de ce qu'elle contient:

objects.stream()
    .filter(element->element instanceof String)
    .map(element->(String)element)
    .collect(Collectors.toList());

1
C'est plus une copie qu'un casting, cependant.
200_success

Le casting mentionné dans l'anser accepté n'a pas fonctionné pour moi. J'étais aussi sur Java 7. Mais Guava a FluentIterablefonctionné pour moi.
Sridhar Sarnobat

C'est ce que je cherchais, je ne connaissais tout simplement pas la syntaxe
Fueled By Coffee

23

Vous pouvez utiliser une double distribution.

return (List<Customer>) (List) getList();

8

Notez que je ne suis pas un programmeur Java, mais en .NET et C #, cette fonctionnalité est appelée contravariance ou covariance. Je n'ai pas encore approfondi ces choses, car elles sont nouvelles dans .NET 4.0, que je n'utilise pas car il n'est que bêta, donc je ne sais pas lequel des deux termes décrit votre problème, mais laissez-moi décrire le problème technique avec cela.

Supposons que vous ayez été autorisé à lancer. Notez, je dis lancer , puisque c'est ce que vous avez dit, mais il y a deux opérations qui pourraient être possibles, le casting et la conversion .

La conversion signifierait que vous obtenez un nouvel objet de liste, mais vous dites casting, ce qui signifie que vous souhaitez traiter temporairement un objet comme un autre type.

Voici le problème avec ça.

Que se passerait-il si ce qui suit était autorisé (note, je suppose qu'avant la distribution, la liste des objets ne contient en fait que des objets Customer, sinon la distribution ne fonctionnerait pas même dans cette version hypothétique de java):

List<Object> list = getList();
List<Customer> customers = (List<Customer>)list;
list.Insert(0, new someOtherObjectNotACustomer());
Customer c = customers[0];

Dans ce cas, cela tenterait de traiter un objet, qui n'est pas un client, comme un client, et vous obtiendriez une erreur d'exécution à un moment donné, soit sous forme à l'intérieur de la liste, soit à partir de l'affectation.

Les génériques, cependant, sont censés vous donner des types de données sécurisés, comme des collections, et comme ils aiment jeter le mot «garanti», ce type de conversion, avec les problèmes qui suivent, n'est pas autorisé.

Dans .NET 4.0 (je sais, votre question portait sur java), cela sera autorisé dans certains cas très spécifiques , où le compilateur peut garantir que les opérations que vous effectuez sont sûres, mais dans le sens général, ce type de distribution ne sera pas être autorisé. Il en va de même pour java, même si je ne suis pas sûr de l'intention d'introduire la co- et la contravariance dans le langage java.

J'espère que quelqu'un avec une meilleure connaissance de Java que moi pourra vous donner les détails pour le futur ou l'implémentation de Java.


3
L'avenir de Java est ... Scala. Sérieusement, le gars qui a boulonné des génériques sur Java a développé un nouveau langage qui est vaguement similaire à Java mais vraiment très compétent avec les types: une implémentation très complète et cohérente de la gestion des types. Je pense que personne ne sait avec certitude quelles fonctionnalités de Scala reviendront dans Java, et quand.
Carl Smotricz

une excellente explication de la covariance qui répond vraiment à la question des OP. Bien joué.
Kevin Day

Carl: Je pensais que certains développeurs Java avaient décidé de créer C #? :) Quoi qu'il en soit, Java va probablement dans le futur vers Scala au lieu de, disons, quelque chose de moins fortement typé.
Esko

@Carl - dans Scala, il y a une différence subtile en ce que les listes par défaut sont immuables. Donc, généralement, vous n'avez pas le problème d'ajouter un objet à une liste de clients, car lorsque vous faites cela, vous obtenez une nouvelle liste d' objets .
Brian Agnew

Euh ... techniquement, c'est correct - mais même avant .NET 4.0 - vous pouviez le faire avec les méthodes d'extension IEnumerable régulières (.Cast <> et .OfType <>) - donc pas besoin de sortir de l'extrémité profonde si vous voulez juste une itération de type forte.
BrainSlugs83

7

Une autre approche consisterait à utiliser un flux java 8.

    List<Customer> customer = myObjects.stream()
                                  .filter(Customer.class::isInstance)
                                  .map(Customer.class::cast)
                                  .collect(toList());

1
merci, cette solution est si belle, en utilisant la méthode de référence
thang

1
Je
n'aurais

3

Vous devez simplement parcourir la liste et lancer tous les objets un par un


3

Tu peux faire quelque chose comme ça

List<Customer> cusList = new ArrayList<Customer>();

for(Object o: list){        
    cusList.add((Customer)o);        
}

return cusList; 

Ou la manière Java 8

list.stream().forEach(x->cusList.add((Customer)x))

return cuslist;

2

Vous ne pouvez pas parce List<Object>que vous List<Customer>n'êtes pas dans le même arbre d'héritage.

Vous pouvez ajouter un nouveau constructeur à votre List<Customer>classe qui prend a List<Object>, puis itère dans la liste en convertissant chacun Objecten a Customeret en l'ajoutant à votre collection. Sachez qu'une exception de conversion non valide peut se produire si l'appelant List<Object>contient quelque chose qui n'est pas unCustomer .

Le but des listes génériques est de les contraindre à certains types. Vous essayez de prendre une liste qui peut contenir n'importe quoi (commandes, produits, etc.) et de la presser dans une liste qui ne peut accepter que des clients.


2

Vous pouvez créer une nouvelle liste et y ajouter les éléments:

Par exemple:

List<A> a = getListOfA();
List<Object> newList = new ArrayList<>();
newList.addAll(a);

1

Votre meilleur pari est de créer un nouveau List<Customer>, de parcourir le List<Object>, d'ajouter chaque élément à la nouvelle liste et de le renvoyer.


1

Comme d'autres l'ont souligné, vous ne pouvez pas les lancer de manière sauve, car a List<Object>n'est pas un List<Customer>. Ce que vous pouvez faire, c'est définir une vue sur la liste qui effectue une vérification de type sur place. En utilisant des collections Google qui seraient:

return Lists.transform(list, new Function<Object, Customer>() {
  public Customer apply(Object from) {
    if (from instanceof Customer) {
      return (Customer)from;
    }
    return null; // or throw an exception, or do something else that makes sense.
  }
});

1

Similaire avec Bozho ci-dessus. Vous pouvez faire une solution de contournement ici (bien que je ne l'aime pas moi-même) via cette méthode:

public <T> List<T> convert(List list, T t){
    return list;
}

Oui. Il convertira votre liste dans le type générique demandé.

Dans le cas donné ci-dessus, vous pouvez faire du code comme celui-ci:

    List<Object> list = getList();
    return convert(list, new Customer());

J'aime cette solution. Même si vous devez ajouter SuppressWarnings, il est préférable de l'ajouter au même endroit que dans chaque diffusion non sécurisée.
robson

1

Selon ce que vous voulez faire de la liste, vous n'aurez peut-être même pas besoin de la convertir en fichier List<Customer>. Si vous souhaitez uniquement ajouter des Customerobjets à la liste, vous pouvez le déclarer comme suit:

...
List<Object> list = getList();
return (List<? super Customer>) list;

C'est légal (enfin, pas seulement légal, mais correct - la liste est "d'un supertype au client"), et si vous allez la transmettre à une méthode qui ajoutera simplement des objets à la liste, alors ce qui précède des bornes génériques sont suffisantes pour cela.

D'un autre côté, si vous souhaitez récupérer des objets de la liste et les faire saisir fortement en tant que clients, vous n'avez pas de chance, et à juste titre. Étant donné que la liste est une, List<Object>il n'y a aucune garantie que le contenu soit des clients, vous devrez donc fournir votre propre casting lors de la récupération. (Ou soyez vraiment, absolument, doublement sûr que la liste ne contiendra Customerset n'utilisera qu'un double cast de l'une des autres réponses, mais réalisez que vous contournez complètement la sécurité de type au moment de la compilation que vous obtenez des génériques dans ce Cas).

D'une manière générale, il est toujours bon de considérer les limites génériques les plus larges possibles qui seraient acceptables lors de l'écriture d'une méthode, doublement si elle doit être utilisée comme méthode de bibliothèque. Si vous ne lisez qu'à partir d'une liste, utilisez List<? extends T>au lieu de List<T>, par exemple - cela donne à vos appelants beaucoup plus de possibilités dans les arguments qu'ils peuvent transmettre et signifie qu'ils sont moins susceptibles de rencontrer des problèmes évitables similaires à celui que vous '' re avoir ici.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.