Jak naprawić błąd „Wyrażenie typu Lista wymaga niesprawdzonej konwersji…”?

137

We fragmencie Java:

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

ostatnia linia generuje ostrzeżenie

„Wyrażenie typu Listwymaga niezaznaczonej konwersji, aby było zgodne z List<SyndEntry>

Jaki jest właściwy sposób, aby to naprawić?

user46277
źródło

Odpowiedzi:

96

Ponieważ getEntrieszwraca surowiec List, może pomieścić wszystko.

Podejście bez ostrzeżeń polega na utworzeniu nowego List<SyndEntry>, a następnie rzutowaniu każdego elementu sf.getEntries()wyniku na SyndEntryprzed dodaniem go do nowej listy. Collections.checkedListczy nie zrobić to sprawdzanie dla ciebie, chociaż byłoby to możliwe do wdrożenia go do tego.

Wykonując własną rzutowanie z góry, „przestrzegasz warunków gwarancji” generycznych Java: jeśli ClassCastExceptionzostanie podniesiona, zostanie skojarzona z rzutowaniem w kodzie źródłowym, a nie niewidocznym rzutem wstawionym przez kompilator.

erickson
źródło
9
Dzięki - to interesujący wgląd w „gwarancję” i niewidoczne rzutowanie wykonane przez kompilator w porównaniu z rzutowaniem wykonanym jawnie w moim własnym kodzie.
user46277
1
Tak, wartość niezreifikowanych leków generycznych jest nieco ograniczona, ale to jedna z rzeczy, które zapewniają. Dla wyjaśnienia, wymaga to kompilacji kodu bez ostrzeżeń dotyczących typu.
erickson
Cześć erickson, zgadzam się, że to rzeczywiście najlepsze rozwiązanie. Sprawdź moją odpowiedź stackoverflow.com/questions/367626/…, aby uzyskać ogólną wersję tego rozwiązania.
Bruno De Fraine
115

Jest to częsty problem w przypadku interfejsów API starszych niż Java 5. Aby zautomatyzować rozwiązanie firmy Erickson , możesz utworzyć następującą metodę ogólną:

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

Dzięki temu możesz:

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

Ponieważ to rozwiązanie sprawdza, czy elementy rzeczywiście mają właściwy typ elementu za pomocą odlewu, jest bezpieczne i nie wymaga SuppressWarnings.

Bruno De Fraine
źródło
5
Jeśli chodzi o metodę sugerowaną przez Bruno, czy nie zaszkodzi to wydajności aplikacji w przypadku list z wieloma elementami? Java musiałaby rzucić każdą z nich.
will824
2
Jeśli chcesz gwarancji, taki jest koszt. Czy jest inna tańsza opcja? Oczywiście, jeśli masz kontrolę nad wywołaną metodą zwracającą surową kolekcję, a nawet wywołujesz metodę lub uzyskujesz dostęp do kolekcji przy użyciu podejścia z opóźnionym żądaniem. Coś, co uwzględnia całą kolekcję po wywołaniu metody?
dan
28

Wygląda na SyndFeedto, że nie używa leków generycznych.

Możesz mieć niebezpieczny rzut i ostrzeżenie:

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

lub zadzwoń do Collections.checkedList - chociaż nadal będziesz musiał pominąć ostrzeżenie:

@SuppressWarnings("unchecked")
List<SyndEntry> entries = Collections.checkedList(sf.getEntries(), SyndEntry.class);
Jon Skeet
źródło
Skoro obaj tłumią ostrzeżenie, jakieś korzyści dla jednej lub drugiej lub preferencje? Dzięki! Ponadto: czy obsada jest konieczna, jeśli niesprawdzone tłumienie jest na miejscu?
Dan Rosenstark
3
@Yar: Cóż, Collections.checkedListzapobiegnie później dodaniu jakichkolwiek elementów spoza SyndEntry. Osobiście nie używam checkedListdużo, ale i tak nie często wpadam w tę niekontrolowaną sytuację rzucania ...
Jon Skeet
9

Napisałeś SyndFeed?

Czy sf.getEntrieslista zwrotów lub List<SyndEntry>? Domyślam się, że wraca, Lista zmiana go na zwrot List<SyndEntry>rozwiązuje problem.

Jeśli SyndFeedjest częścią biblioteki, nie sądzę, aby można było usunąć ostrzeżenie bez dodawania @SuppressWarning("unchecked")adnotacji do metody.

Alex B.
źródło
Możesz także dodać wyraźną obsadę.
Uri
3
Rzutowanie spowoduje po prostu kolejne ostrzeżenie, ponieważ kod nie jest bezpieczny dla typów.
erickson
SyndFeedpochodzi z rometools.github.io/rome/ROMEReleases/ROME1.0Release.html . Wydaje się, że problem został rozwiązany w nowszych wersjach Rzymu, takich jak te znalezione na mvnrepository.com/artifact/com.rometools/rome/1.9.0
daloonik
2

Jeśli używasz guawy i wszystko, co chcesz zrobić, to powtórzyć swoje wartości:

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

Jeśli potrzebujesz aktualnej listy, możesz użyć

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

lub

List<SyndEntry> list = ImmutableList.copyOf(
    Iterables.filter(sf.getEntries(), SyndEntry.class));
Joseph K. Strauss
źródło
1
SyndFeedInput fr = new SyndFeedInput();
SyndFeed sf = fr.build(new XmlReader(myInputStream));
List<?> entries = sf.getEntries();
Honglonglong
źródło
2
Nawet jeśli podany tutaj kod rozwiązuje problem, zachęcam do krótkiego wyjaśnienia, dlaczego tak się dzieje. Proszę wyjaśnić, dlaczego opublikowana odpowiedź rozwiązuje problem.
sbrattla
1

Jeśli spojrzysz na javadoc dla klasy SyndFeed(myślę com.sun.syndication.feed.synd.SyndFeed, że odnosisz się do klasy ), metoda getEntries () nie zwraca java.util.List<SyndEntry>, ale po prostu zwraca java.util.List.

Potrzebujesz więc do tego wyraźnej obsady.

Shyam
źródło
0

Jeśli nie chcesz umieszczać @SuppressWarning ("unchecked") przy każdym wywołaniu sf.getEntries (), zawsze możesz utworzyć opakowanie, które zwróci List.

Zobacz to inne pytanie

Boune
źródło
0

Jeszcze łatwiej

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

DennisTemper
źródło
Następnie zajmowałbyś się odpowiednim rzutowaniem (ponownym rzutowaniem?) W czasie użycia dla każdego elementu w ArrayList <?>.
ingyhere