¿Cómo soluciono "La expresión de tipo Lista necesita una conversión no verificada ..."?

137

En el fragmento de Java:

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

la última línea genera la advertencia

"La expresión de tipo Listnecesita una conversión sin marcar para ajustarse a List<SyndEntry>"

¿Cuál es una forma apropiada de arreglar esto?

usuario46277
fuente

Respuestas:

96

Como getEntriesdevuelve un raw List, podría contener cualquier cosa.

El enfoque sin advertencia es crear uno nuevo List<SyndEntry>, luego emitir cada elemento del sf.getEntries()resultado SyndEntryantes de agregarlo a su nueva lista. Collections.checkedListno no hacer esta comprobación para usted-aunque habría sido posible ponerlo en práctica para hacerlo.

Al hacer su propia conversión por adelantado, está "cumpliendo con los términos de garantía" de los genéricos de Java: si ClassCastExceptionse genera una, se asociará con una conversión en el código fuente, no con una conversión invisible insertada por el compilador.

erickson
fuente
9
Gracias, es una idea interesante sobre la "garantía" y el reparto invisible realizado por el compilador frente a un reparto explícito en mi propio código.
user46277
1
Sí, el valor de los genéricos no reificados es algo limitado, pero eso es algo que sí proporciona. Solo para aclarar, esto requiere que su código se compile sin advertencias de seguridad de tipo.
erickson
Hola Erickson, estoy de acuerdo en que esta es la mejor solución. Consulte mi respuesta stackoverflow.com/questions/367626/… para obtener una versión genérica de esta solución.
Bruno De Fraine
115

Este es un problema común cuando se trata de API anteriores a Java 5. Para automatizar la solución de erickson , puede crear el siguiente 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;
}

Esto te permite hacer:

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

Debido a que esta solución verifica que los elementos tengan el tipo de elemento correcto por medio de un molde, es seguro y no requiere SuppressWarnings.

Bruno De Fraine
fuente
55
Con respecto al método sugerido por Bruno, ¿no afectaría esto el rendimiento de la aplicación al tener listas con muchos elementos? Java tendría que lanzar todos y cada uno de ellos.
will824
2
Si quieres garantías, ese es el costo. ¿Hay otra opción menos costosa? Obviamente, si tiene control sobre el método de devolución de la colección sin procesar invocada, o incluso invocando el método o accediendo a la colección utilizando un enfoque de demanda perezosa. ¿Algo que considere la colección completa después de la invocación del método?
dan
28

Parece que SyndFeedno está usando genéricos.

Podría tener un yeso inseguro y una supresión de advertencia:

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

o llame a Collections.checkedList , aunque aún deberá suprimir la advertencia:

@SuppressWarnings("unchecked")
List<SyndEntry> entries = Collections.checkedList(sf.getEntries(), SyndEntry.class);
Jon Skeet
fuente
Dado que ambos suprimen la advertencia, ¿hay alguna ventaja para uno u otro, o una preferencia? ¡Gracias! Además: ¿es necesario el reparto si la supresión no controlada está en su lugar?
Dan Rosenstark
3
@Yar: Bueno, Collections.checkedListevitará la adición de elementos que no sean SyndEntry más adelante. Yo personalmente no uso checkedListmucho, pero tampoco me meto en esta situación de lanzamiento sin control de todos modos ...
Jon Skeet
9

¿Escribiste el SyndFeed?

¿ sf.getEntriesDevuelve la lista o List<SyndEntry>? Supongo que regresa Listy cambiarlo para que regrese List<SyndEntry>solucionará el problema.

Si SyndFeedes parte de una biblioteca, no creo que pueda eliminar la advertencia sin agregar la @SuppressWarning("unchecked")anotación a su método.

Alex B
fuente
También puede agregar un reparto explícito.
Uri
3
Un reparto solo generará otra advertencia, ya que el código no es de tipo seguro.
erickson
SyndFeedproviene de rometools.github.io/rome/ROMEReleases/ROME1.0Release.html . El problema parece solucionarse en versiones más recientes de Roma como las que se encuentran en mvnrepository.com/artifact/com.rometools/rome/1.9.0
daloonik
2

Si está usando Guava y todo lo que quiere hacer es iterar a través de sus valores:

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

Si necesita una lista real, puede usar

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

o

List<SyndEntry> list = ImmutableList.copyOf(
    Iterables.filter(sf.getEntries(), SyndEntry.class));
Joseph K. Strauss
fuente
1
SyndFeedInput fr = new SyndFeedInput();
SyndFeed sf = fr.build(new XmlReader(myInputStream));
List<?> entries = sf.getEntries();
Honglonglong
fuente
2
Incluso si el código proporcionado aquí resuelve el problema, le animo a que explique brevemente por qué lo hace. Explica por qué la respuesta publicada resuelve el problema.
sbrattla
1

Si observas el javadoc para la clase SyndFeed(supongo que te estás refiriendo a la clase com.sun.syndication.feed.synd.SyndFeed), el método getEntries () no regresa java.util.List<SyndEntry>, sino que solo devuelve java.util.List.

Entonces necesitas un reparto explícito para esto.

Shyam
fuente
0

Si no desea poner @SuppressWarning ("sin marcar") en cada llamada sf.getEntries (), siempre puede hacer un contenedor que devolverá List.

Ver esta otra pregunta

Boune
fuente
0

Aún más fácil

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

DennisTemper
fuente
Entonces se ocuparía de la conversión adecuada (¿re-conversión?) En el momento de uso de cada elemento en ArrayList <?>.
ingyhere