Come eseguire il cast di List <Object> in List <MyClass>


Risposte:


156

puoi sempre lanciare qualsiasi oggetto su qualsiasi tipo eseguendo prima l'up-casting su Object. nel tuo caso:

(List<Customer>)(Object)list; 

bisogna essere sicuri che in fase di esecuzione l'elenco non contenga altro che oggetti Cliente.

I critici dicono che tale casting indica qualcosa di sbagliato nel tuo codice; dovresti essere in grado di modificare le tue dichiarazioni di tipo per evitarlo. Ma i generici Java sono troppo complicati e non sono perfetti. A volte semplicemente non sai se esiste una soluzione carina per soddisfare il compilatore, anche se conosci molto bene i tipi di runtime e sai che quello che stai cercando di fare è sicuro. In tal caso, basta eseguire il casting grezzo secondo necessità, in modo da poter lasciare il lavoro per casa.


8
Anche questo ha bisogno @SuppressWarnings("unchecked"). Nota che puoi anche eseguire l'upcast a (List)invece che a (Object).
200_success

36

Questo perché sebbene un cliente sia un oggetto, un elenco di clienti non è un elenco di oggetti. Se lo fosse, potresti inserire qualsiasi oggetto in un elenco di clienti.


2
No, non lo è. Si aggirerebbe l'indipendenza dai tipi se quanto sopra fosse consentito. Nota che il casting non significa creare un nuovo elenco e copiare gli elementi. Significa gestire la singola istanza come un tipo diverso, e quindi si avrebbe un elenco che contiene oggetti potenzialmente non Customer con una garanzia di sicurezza dei tipi che non dovrebbe. Questo non ha nulla a che fare con Java in quanto tale. Dal momento che ritieni che questa funzione lasci Java bloccato nel medioevo, ti sfido a spiegare come pensi che dovrebbe funzionare invece.
Lasse V. Karlsen,

1
@ BrainSlugs83 per le tue necessità (List <Customer>) (Object) list;
Muthu Ganapathy Nathan

@ LasseV.Karlsen Non sto parlando di casting, sto parlando di conversione. Non aggirerebbe l'indipendenza dai tipi, ma la rafforzerebbe. L'implementazione di generici in Java, la mancanza di sovraccarichi di operatori, metodi di estensione e molti altri servizi moderni mi ha lasciato epicamente deluso. - Nel frattempo, .NET ha due metodi di estensione separati per questo: uno chiamato .Cast<T>()e uno chiamato .OfType<T>(). Il primo esegue un cast su ogni elemento (generando le eccezioni desiderate) mentre il secondo filtra gli elementi che non possono essere cast (quindi ne sceglierai uno a seconda dello scenario di utilizzo).
BrainSlugs83

1
@EAGER_STUDENT Non lo metterei oltre Java che potrebbe effettivamente funzionare (tutto questo ridicolo lavoro di cancellazione di tipo, dovrei provarlo ...) - ma no, non scriverei mai codice come quello - perdi protezione dai tipi, cosa succede se un elemento della collezione non è un instanceofcliente?
BrainSlugs83

1
@ BrainSlugs83 La persona che ha posto la domanda ha chiesto specificatamente sul casting. E se Java non ha già i metodi pertinenti come i metodi .NET a cui ti riferisci (che ancora non sta convertendo btw), allora dovresti essere facilmente in grado di aggiungerli. Questo è tutto un po 'ortogonale alla domanda in esame, che chiedeva informazioni sulla sintassi specifica.
Lasse V. Karlsen

35

A seconda dell'altro codice, la risposta migliore può variare. Provare:

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

o

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

Ma tieni presente che non è consigliabile eseguire lanci così incontrollati.


3
-1 - questa è davvero una brutta abitudine da prendere e, a meno che tu non usi l'annotazione "Unchecked", il compilatore si lamenterà comunque
kdgregory

24

Con Java 8 Stream :

A volte il casting a forza bruta va bene:

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

Ma ecco una soluzione più versatile:

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

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

Ci sono un sacco di vantaggi, ma uno è che puoi trasmettere la tua lista in modo più elegante se non puoi essere sicuro di cosa contiene:

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

1
È più una copia che un cast, però.
200_success

Il casting menzionato nell'anser accettato non ha funzionato per me. Inoltre ero su Java 7. Ma Guava ha FluentIterablefunzionato per me.
Sridhar Sarnobat

Questo è quello che stavo cercando, semplicemente non conoscevo la sintassi
Fueled By Coffee

23

Puoi usare un doppio cast.

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

8

Si noti che non sono un programmatore Java, ma in .NET e C # questa funzionalità è chiamata controvarianza o covarianza. Non ho ancora approfondito queste cose, poiché sono nuove in .NET 4.0, che non sto usando poiché è solo beta, quindi non so quale dei due termini descrive il tuo problema, ma lasciami descrivere il problema tecnico con questo.

Supponiamo che ti sia stato permesso di trasmettere. Nota, dico cast , poiché è quello che hai detto, ma ci sono due operazioni che potrebbero essere possibili, casting e conversione .

La conversione significherebbe che si ottiene un nuovo oggetto elenco, ma si dice casting, il che significa che si desidera trattare temporaneamente un oggetto come un altro tipo.

Ecco il problema con quello.

Cosa accadrebbe se fosse consentito quanto segue (nota, presumo che prima del cast, l'elenco degli oggetti in realtà contenga solo oggetti Customer, altrimenti il ​​cast non funzionerebbe anche in questa ipotetica versione di java):

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

In questo caso, questo tenterebbe di trattare un oggetto, che non è un cliente, come un cliente, e si otterrebbe un errore di runtime a un certo punto, sia dal modulo all'interno dell'elenco, sia dall'assegnazione.

I generici, tuttavia, dovrebbero fornire tipi di dati indipendenti dai tipi, come le raccolte, e poiché a loro piace lanciare la parola "garantito" in giro, questo tipo di cast, con i problemi che seguono, non è consentito.

In .NET 4.0 (lo so, la tua domanda riguardava java), questo sarà consentito in alcuni casi molto specifici , dove il compilatore può garantire che le operazioni che fai sono sicure, ma in generale, questo tipo di cast non lo farà essere consentito. Lo stesso vale per java, anche se non sono sicuro di eventuali piani per introdurre co e controvarianza nel linguaggio java.

Si spera che qualcuno con una conoscenza java migliore di me possa dirti le specifiche per il futuro o l'implementazione di Java.


3
Il futuro di Java è ... Scala. Scherzi a parte, il ragazzo che ha imbullonato i generici su Java ha sviluppato un nuovo linguaggio che è vagamente simile a Java ma davvero, davvero competente con i tipi: un'implementazione molto completa e coerente della gestione dei tipi. Penso che nessuno sappia con certezza quali funzionalità di Scala torneranno in Java e quando.
Carl Smotricz

un'ottima spiegazione della covarianza che risponde veramente alla domanda dei PO. Molto bene.
Kevin Day

Carl: Pensavo che alcuni sviluppatori Java fossero andati avanti per creare C #? :) Comunque sì, molto probabilmente Java andrà nella direzione di Scala in futuro invece di, diciamo, qualcosa di meno fortemente tipizzato.
Esko

@Carl - in Scala c'è una sottile differenza nel fatto che per impostazione predefinita le liste sono immutabili. Quindi generalmente non hai il problema di aggiungere un oggetto a un elenco di clienti, poiché quando lo fai ottieni un nuovo elenco di oggetti .
Brian Agnew

Ehm ... tecnicamente questo è corretto, ma anche prima di .NET 4.0, potresti farlo con i normali metodi di estensione IEnumerable (.Cast <> e .OfType <>), quindi non c'è bisogno di andare oltre se vuoi solo un'iterazione di tipo forte.
BrainSlugs83

7

Un altro approccio potrebbe essere l'utilizzo di un flusso java 8.

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

1
grazie, questa soluzione è così bella, utilizzando il metodo di riferimento
thang

1
Non
avrei

3

Dovresti semplicemente scorrere l'elenco e lanciare tutti gli oggetti uno per uno


3

Puoi fare qualcosa del genere

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

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

return cusList; 

O il modo Java 8

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

return cuslist;

2

Non puoi perché List<Object>e List<Customer>non sono nello stesso albero dell'eredità.

Potresti aggiungere un nuovo costruttore alla tua List<Customer>classe che prende a List<Object>e quindi itera attraverso l'elenco eseguendo il casting di ciascuno Objectin a Customere aggiungendolo alla tua raccolta. Tieni presente che può verificarsi un'eccezione di cast non valida se il chiamante List<Object>contiene qualcosa che non è un file Customer.

Lo scopo degli elenchi generici è di vincolarli a determinati tipi. Stai cercando di prendere un elenco che può contenere qualsiasi cosa (ordini, prodotti, ecc.) E comprimerlo in un elenco che può contenere solo clienti.


2

Puoi creare un nuovo elenco e aggiungervi gli elementi:

Per esempio:

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

1

La soluzione migliore è crearne uno nuovo List<Customer>, scorrere il List<Object>, aggiungere ogni elemento al nuovo elenco e restituirlo.


1

Come altri hanno sottolineato, non puoi lanciarli con cautela, poiché a List<Object>non è a List<Customer>. Quello che potresti fare è definire una vista nell'elenco che esegua il controllo del tipo sul posto. Utilizzando le raccolte di Google che sarebbe:

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

Simile a Bozho sopra. Puoi fare qualche soluzione qui (anche se a me stesso non piace) attraverso questo metodo:

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

Sì. Trasmetterà la tua lista nel tipo generico richiesto.

Nel caso indicato sopra, puoi fare un codice come questo:

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

Mi piace questa soluzione. Anche se è necessario aggiungere SuppressWarnings, è meglio aggiungerlo in un punto piuttosto che in ogni casting non sicuro.
robson

1

A seconda di cosa vuoi fare con l'elenco, potresti non aver nemmeno bisogno di trasmetterlo a un file List<Customer>. Se vuoi solo aggiungere Customeroggetti alla lista, puoi dichiararlo come segue:

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

Questo è legale (beh, non solo legale, ma corretto - l'elenco è di "qualche supertipo per il cliente") e se lo passerai a un metodo che si limiterà ad aggiungere oggetti all'elenco, allora quanto sopra limiti generici sono sufficienti per questo.

D'altra parte, se vuoi recuperare oggetti dalla lista e averli fortemente digitati come Clienti, allora sei sfortunato, ed è giusto che sia così. Poiché l'elenco è un List<Object>non vi è alcuna garanzia che i contenuti siano clienti, quindi dovrai fornire il tuo casting al momento del recupero. (O sii davvero, assolutamente, doppiamente sicuro che l'elenco conterrà Customerse utilizzerà solo un cast doppio da una delle altre risposte, ma renditi conto che stai completamente aggirando la sicurezza dei tipi in fase di compilazione che ottieni dai generici in questo Astuccio).

In generale, è sempre bene considerare i limiti generici più ampi possibili che sarebbero accettabili quando si scrive un metodo, doppiamente se verrà utilizzato come metodo di libreria. Se hai intenzione di leggere solo da un elenco, usa List<? extends T>invece di List<T>, ad esempio: questo offre ai chiamanti molto più ambito negli argomenti che possono passare e significa che hanno meno probabilità di incorrere in problemi evitabili simili a quello che tu '' stai avendo qui.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.