Questo non compila, qualsiasi suggerimento apprezzato.
...
List<Object> list = getList();
return (List<Customer>) list;
Il compilatore dice: impossibile eseguire il cast List<Object>
suList<Customer>
Questo non compila, qualsiasi suggerimento apprezzato.
...
List<Object> list = getList();
return (List<Customer>) list;
Il compilatore dice: impossibile eseguire il cast List<Object>
suList<Customer>
Risposte:
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.
@SuppressWarnings("unchecked")
. Nota che puoi anche eseguire l'upcast a (List)
invece che a (Object)
.
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.
.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).
instanceof
cliente?
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.
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());
FluentIterable
funzionato per me.
Puoi usare un doppio cast.
return (List<Customer>) (List) getList();
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.
List<Customer> cusList = new ArrayList<Customer>();
for(Object o: list){
cusList.add((Customer)o);
}
return cusList;
list.stream().forEach(x->cusList.add((Customer)x))
return cuslist;
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 Object
in a Customer
e 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.
La soluzione migliore è crearne uno nuovo List<Customer>
, scorrere il List<Object>
, aggiungere ogni elemento al nuovo elenco e restituirlo.
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.
}
});
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());
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 Customer
oggetti 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à Customers
e 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.