Come posso risolvere "L'espressione del tipo Elenco richiede una conversione non selezionata ... '?


137

Nel frammento Java:

SyndFeedInput fr = new SyndFeedInput();
SyndFeed sf = fr.build(new XmlReader(myInputStream));
List<SyndEntry> entries = sf.getEntries();

l'ultima riga genera l'avviso

"L'espressione di tipo Listrichiede una conversione non selezionata per conformarsi a List<SyndEntry>"

Qual è un modo appropriato per risolvere questo problema?

Risposte:


96

Dal momento che getEntriesrestituisce un raw List, potrebbe contenere qualsiasi cosa.

L'approccio privo di avvisi è quello di creare un nuovo List<SyndEntry>, quindi eseguire il cast di ogni elemento del sf.getEntries()risultato SyndEntryprima di aggiungerlo al nuovo elenco. Collections.checkedListnon non fare questo controllo per voi, anche se sarebbe stato possibile per la sua attuazione a farlo.

Facendo il tuo cast up front, stai "rispettando i termini di garanzia" dei generici Java: se ClassCastExceptionviene generato, verrà associato a un cast nel codice sorgente, non a un cast invisibile inserito dal compilatore.


9
Grazie - questa è una visione interessante della "garanzia" e del cast invisibile fatto dal compilatore rispetto a un cast fatto esplicitamente nel mio codice.
user46277

1
Sì, il valore dei generici non reificati è alquanto limitato, ma questa è una cosa che fornisce. Solo per chiarire, ciò richiede che il codice venga compilato senza avvisi di sicurezza del tipo.
Erickson,

Ciao Erickson, sono d'accordo che questa sia davvero la soluzione migliore. Controlla la mia risposta stackoverflow.com/questions/367626/… per una versione generica di questa soluzione.
Bruno De Fraine,

115

Questo è un problema comune quando si ha a che fare con API pre-Java 5. Per automatizzare la soluzione da Erickson , è possibile creare il seguente metodo generico:

public static <T> List<T> castList(Class<? extends T> clazz, Collection<?> c) {
    List<T> r = new ArrayList<T>(c.size());
    for(Object o: c)
      r.add(clazz.cast(o));
    return r;
}

Questo ti permette di fare:

List<SyndEntry> entries = castList(SyndEntry.class, sf.getEntries());

Poiché questa soluzione verifica che gli elementi abbiano effettivamente il tipo di elemento corretto mediante un cast, è sicuro e non richiede SuppressWarnings.


5
Per quanto riguarda il metodo suggerito da Bruno, ciò non danneggerebbe le prestazioni dell'applicazione quando si hanno elenchi con molti elementi ?. Java dovrebbe lanciare ognuno di loro.
will824,

2
Se vuoi garanzie, questo è il costo. C'è un'altra opzione meno costosa? Ovviamente, se si ha il controllo sul metodo di restituzione della raccolta non elaborata invocato, o addirittura si richiama il metodo o si accede alla raccolta utilizzando un approccio a richiesta pigra. Qualcosa che considera l'intera raccolta dopo l'invocazione del metodo?
dan

28

Sembra che SyndFeednon stia usando i generici.

Potresti avere un cast non sicuro e una soppressione degli avvisi:

@SuppressWarnings("unchecked")
List<SyndEntry> entries = (List<SyndEntry>) sf.getEntries();

o chiama Collections.checkedList , anche se dovrai comunque eliminare l'avviso:

@SuppressWarnings("unchecked")
List<SyndEntry> entries = Collections.checkedList(sf.getEntries(), SyndEntry.class);

Dal momento che entrambi sopprimono l'avvertimento, qualche vantaggio per l'uno o l'altro o una preferenza? Grazie! Inoltre: il cast è necessario se la soppressione incontrollata è in atto?
Dan Rosenstark,

3
@Yar: Bene, Collections.checkedListimpedirà l'aggiunta di eventuali elementi non SyndEntry in seguito. Personalmente non uso checkedListmolto, ma poi non mi capita spesso di entrare in questa situazione di controllo incontrollata ...
Jon Skeet,

9

Hai scritto il SyndFeed?

Fa sf.getEntriestornare List o List<SyndEntry>? La mia ipotesi è che ritorni Liste cambiarlo per tornare List<SyndEntry>risolverà il problema.

Se SyndFeedfa parte di una libreria, non penso che puoi rimuovere l'avviso senza aggiungere l' @SuppressWarning("unchecked")annotazione al tuo metodo.


Puoi anche aggiungere un cast esplicito.
Uri,

3
Un cast produrrà solo un altro avvertimento, poiché il codice non è sicuro.
Erickson,

SyndFeedproviene da rometools.github.io/rome/ROMEReleases/ROME1.0Release.html . Il problema sembra essere stato risolto in versioni più recenti di Roma come quelle trovate su mvnrepository.com/artifact/com.rometools/rome/1.9.0
daloonik

2

Se stai usando Guava e tutto ciò che vuoi fare è scorrere i tuoi valori:

for(SyndEntry entry: Iterables.filter(sf.getEntries(), SyndEntry.class){
  ...
}

Se hai bisogno di un elenco reale, puoi usarlo

List<SyndEntry> list = Lists.newArrayList(
    Iterables.filter(sf.getEntries(), SyndEntry.class));

o

List<SyndEntry> list = ImmutableList.copyOf(
    Iterables.filter(sf.getEntries(), SyndEntry.class));

1
SyndFeedInput fr = new SyndFeedInput();
SyndFeed sf = fr.build(new XmlReader(myInputStream));
List<?> entries = sf.getEntries();

2
Anche se il codice fornito qui risolve il problema, ti incoraggio a spiegare brevemente perché lo fa. Spiegare perché la risposta pubblicata risolve il problema.
sbrattla,

1

Se guardi il javadoc per la classe SyndFeed(suppongo che ti riferisci alla classe com.sun.syndication.feed.synd.SyndFeed), il metodo getEntries () non restituisce java.util.List<SyndEntry>, ma restituisce semplicemente java.util.List.

Quindi hai bisogno di un cast esplicito per questo.


0

Se non vuoi mettere @SuppressWarning ("deselezionato") su ogni chiamata sf.getEntries (), puoi sempre creare un wrapper che restituirà List.

Vedi questa altra domanda


0

Ancora più facile

return new ArrayList<?>(getResultOfHibernateCallback(...))


Quindi dovrai occuparti del casting adeguato (re-casting?) Al momento dell'utilizzo per ciascun elemento in ArrayList <?>.
ingyhere il
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.