Como corrijo "A expressão do tipo Lista precisa de conversão desmarcada ... '?

137

No fragmento de Java:

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

a última linha gera o aviso

"A expressão do tipo Listprecisa de conversão desmarcada para estar em conformidade com List<SyndEntry>"

Qual é a maneira apropriada de corrigir isso?

user46277
fonte

Respostas:

96

Como getEntriesretorna um raw List, ele pode conter qualquer coisa.

A abordagem sem aviso é criar um novo List<SyndEntry>e converter cada elemento do sf.getEntries()resultado SyndEntryantes de adicioná-lo à sua nova lista. Collections.checkedListse não fazer esta verificação para você, embora teria sido possível aplicá-la a fazê-lo.

Ao fazer sua própria conversão antecipadamente, você está "cumprindo os termos de garantia" dos genéricos Java: se a ClassCastExceptionfor aumentada, ela será associada a uma conversão no código-fonte, não a uma conversão invisível inserida pelo compilador.

erickson
fonte
9
Obrigado - essa é uma visão interessante sobre a "garantia" e o elenco invisível feito pelo compilador versus um elenco explicitamente feito no meu próprio código.
User46277
1
Sim, o valor dos genéricos não reificados é um pouco limitado, mas isso é uma coisa que ele fornece. Apenas para esclarecer, isso exige que seu código seja compilado sem avisos de segurança de tipo.
Erickson
Oi Erickson, concordo que esta é realmente a melhor solução. Verifique minha resposta stackoverflow.com/questions/367626/… para obter uma versão genérica desta solução.
Bruno De Fraine
115

Esse é um problema comum ao lidar com APIs pré-Java 5. Para automatizar a solução a partir de erickson , você pode criar o seguinte método genérico:

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;
}

Isso permite que você faça:

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

Como esta solução verifica se os elementos realmente têm o tipo correto de elemento por meio de uma conversão, é seguro e não requer SuppressWarnings.

Bruno De Fraine
fonte
5
Com relação ao método sugerido por Bruno, isso não afetaria o desempenho do aplicativo ao ter Listas com muitos elementos ?. Java teria que lançar cada um deles.
precisa saber é
2
Se você quer garantias, esse é o custo. Existe outra opção menos cara? Obviamente, se você tiver controle sobre o método de retorno de coleção bruta invocado, ou mesmo invocando o método ou acessando a coleção usando uma abordagem de demanda lenta. Algo que considera a coleção inteira após a invocação do método?
dan
28

Parece que SyndFeednão está usando genéricos.

Você pode ter um elenco inseguro e uma supressão de aviso:

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

ou chame Collections.checkedList - embora você ainda precise suprimir o aviso:

@SuppressWarnings("unchecked")
List<SyndEntry> entries = Collections.checkedList(sf.getEntries(), SyndEntry.class);
Jon Skeet
fonte
Como ambos suprimem o aviso, há vantagens para um ou outro, ou uma preferência? Obrigado! Além disso: o elenco é necessário se a supressão desmarcada estiver em vigor?
Dan Rosenstark
3
@ Yar: Bem, Collections.checkedListimpedirá a adição de qualquer elemento não SyndEntry posteriormente. Eu pessoalmente não uso checkedListmuito, mas então eu também não costumam chegar a esta situação elenco desmarcada qualquer maneira ...
Jon Skeet
9

Você escreveu o SyndFeed?

Retorna sf.getEntriesLista ou List<SyndEntry>? Meu palpite é que ele retorna Liste alterá-lo para retornar List<SyndEntry>corrigirá o problema.

Se SyndFeedfaz parte de uma biblioteca, acho que você não pode remover o aviso sem adicionar a @SuppressWarning("unchecked")anotação ao seu método.

Alex B
fonte
Você também pode adicionar uma conversão explícita.
Uri
3
Um elenco produzirá apenas outro aviso, pois o código não é do tipo seguro.
21480
SyndFeedvem de rometools.github.io/rome/ROMEReleases/ROME1.0Release.html . O problema parece estar corrigido em versões mais recentes de Roma, como as encontradas em mvnrepository.com/artifact/com.rometools/rome/1.9.0
daloonik:
2

Se você estiver usando o Guava e tudo o que você deseja fazer é percorrer seus valores:

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

Se você precisar de uma lista real, poderá usar

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

ou

List<SyndEntry> list = ImmutableList.copyOf(
    Iterables.filter(sf.getEntries(), SyndEntry.class));
Joseph K. Strauss
fonte
1
SyndFeedInput fr = new SyndFeedInput();
SyndFeed sf = fr.build(new XmlReader(myInputStream));
List<?> entries = sf.getEntries();
Honglonglong
fonte
2
Mesmo que o código fornecido aqui resolva o problema, sugiro que você explique brevemente por que isso ocorre. Explique por que a resposta postada resolve o problema.
Sbrattla
1

Se você olhar o javadoc para a classe SyndFeed(acho que você está se referindo à classe com.sun.syndication.feed.synd.SyndFeed), o método getEntries () não retornará java.util.List<SyndEntry>, mas retornará apenas java.util.List.

Então você precisa de um elenco explícito para isso.

Shyam
fonte
0

Se você não deseja colocar @SuppressWarning ("unchecked") em cada chamada sf.getEntries (), você sempre pode criar um wrapper que retornará List.

Veja esta outra pergunta

Boune
fonte
0

Ainda mais fácil

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

DennisTemper
fonte
Em seguida, você lidaria com a conversão adequada (re-conversão?) No tempo de uso para cada elemento em ArrayList <?>.
precisa saber é