Eclipse wyświetla mi ostrzeżenie o następującej formie:
Bezpieczeństwo typu: Niezaznaczone rzutowanie z Object na HashMap
Wynika to z wywołania interfejsu API, nad którym nie mam kontroli, który zwraca obiekt:
HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
HashMap<String, String> theHash = (HashMap<String, String>)session.getAttribute("attributeKey");
return theHash;
}
Jeśli to możliwe, chciałbym unikać ostrzeżeń Eclipse, ponieważ teoretycznie wskazują one przynajmniej na potencjalny problem z kodem. Jednak nie znalazłem jeszcze dobrego sposobu na wyeliminowanie tego. Mogę wyodrębnić pojedynczy wiersz związany z metodą i dodać ją @SuppressWarnings("unchecked")
do tej metody, ograniczając w ten sposób wpływ bloku kodu, w którym ignoruję ostrzeżenia. Jakieś lepsze opcje? Nie chcę wyłączać tych ostrzeżeń w Eclipse.
Zanim przyszedłem do kodu, było to prostsze, ale wciąż prowokowało ostrzeżenia:
HashMap getItems(javax.servlet.http.HttpSession session) {
HashMap theHash = (HashMap)session.getAttribute("attributeKey");
return theHash;
}
Problem polegał na tym, że gdy próbowałeś użyć skrótu, pojawiały się ostrzeżenia:
HashMap items = getItems(session);
items.put("this", "that");
Type safety: The method put(Object, Object) belongs to the raw type HashMap. References to generic type HashMap<K,V> should be parameterized.
enum
Lub nawet przypadkiClass<T>
), abyś mógł na nią spojrzeć i wiedzieć, że jest bezpieczny.Odpowiedzi:
Oczywistą odpowiedzią nie jest oczywiście wykonanie niezatwierdzonej obsady.
Jeśli jest to absolutnie konieczne, spróbuj przynajmniej ograniczyć zakres
@SuppressWarnings
adnotacji. Według Javadocs może on zmieniać zmienne lokalne; w ten sposób nie wpływa to nawet na całą metodę.Przykład:
Nie ma sposobu, aby ustalić, czy
Map
naprawdę powinny mieć ogólne parametry<String, String>
. Musisz wcześniej wiedzieć, jakie powinny być parametry (lub dowiesz się, kiedy otrzymasz aClassCastException
). Dlatego kod generuje ostrzeżenie, ponieważ kompilator nie może wiedzieć, czy jest bezpieczny.źródło
String s = (String) new Object() ;
nie dostaje ostrzeżenia, mimo że kompilator nie wie, że rzutowanie jest bezpieczne. Ostrzeżenie jest spowodowane tym, że kompilator (a) nie wie, że rzutowanie jest bezpieczne ORAZ (b) nie wygeneruje pełnej kontroli czasu działania w punkcie rzutowania. Zostanie sprawdzony, czy to jestHashmap
, ale nie będzie czeku, że to jestHashMap<String,String>
.Niestety nie ma tutaj świetnych opcji. Pamiętaj, że celem tego wszystkiego jest zachowanie bezpieczeństwa typu. „ Java Generics ” oferuje rozwiązanie do radzenia sobie z nieogenerowanymi bibliotekami starszego typu, aw szczególności w części 8.2 nazywa się ją „techniką pustej pętli”. Zasadniczo wykonaj niebezpieczną obsadę i ukryj ostrzeżenie. Następnie przejdź przez mapę w następujący sposób:
Jeśli napotkasz nieoczekiwany typ, otrzymasz środowisko wykonawcze
ClassCastException
, ale przynajmniej stanie się to blisko źródła problemu.źródło
Łał; Myślę, że wymyśliłem odpowiedź na moje własne pytanie. Po prostu nie jestem pewien, czy warto! :)
Problem polega na tym, że obsada nie jest sprawdzana. Musisz to sprawdzić sam. Nie można po prostu sprawdzić sparametryzowanego typu za pomocą instanceof, ponieważ informacje o sparametryzowanym typie są niedostępne w czasie wykonywania, ponieważ zostały usunięte podczas kompilacji.
Ale możesz wykonać sprawdzenie każdego elementu w skrócie za pomocą instanceof, a dzięki temu możesz zbudować nowy skrót, który jest bezpieczny dla typu. I nie wywołasz żadnych ostrzeżeń.
Dzięki mmyers i Esko Luontoli sparametryzowałem kod, który tu pierwotnie napisałem, aby można go było gdzieś zawinąć w klasę użyteczności i wykorzystać w dowolnym sparametryzowanym HashMap. Jeśli chcesz to lepiej zrozumieć i nie znasz zbyt dobrze ogólnych, zachęcam do przejrzenia historii edycji tej odpowiedzi.
To dużo pracy, być może za bardzo małą nagrodę ... Nie jestem pewien, czy z niej skorzystam, czy nie. Będę wdzięczny za wszelkie komentarze dotyczące tego, czy ludzie uważają, że warto, czy nie. Będę również wdzięczny za sugestie dotyczące ulepszeń: czy jest coś lepszego, co mogę zrobić oprócz wyrzucenia błędów AssertionErrors? Czy jest coś lepszego, co mógłbym rzucić? Czy powinienem zrobić z tego sprawdzony wyjątek?
źródło
W Preferencjach Eclipse przejdź do Java-> Kompilator-> Błędy / Ostrzeżenia-> Typy ogólne i zaznacz pole
Ignore unavoidable generic type problems
wyboru.To spełnia cel pytania, tj
jeśli nie duch.
źródło
uses unchecked or unsafe operations.
błąd „ ”javac
, ale dodanie@SuppressWarnings("unchecked")
spowodowało, że Eclipse był niezadowolony, twierdząc, że tłumienie było niepotrzebne. Odznaczenie tego pola powoduje, że Eclipsejavac
zachowuje się tak samo, i tego właśnie chciałem. Jawne pomijanie ostrzeżenia w kodzie jest znacznie wyraźniejsze niż pomijanie go wszędzie w środowisku Eclipse.Możesz utworzyć klasę narzędzia taką jak poniżej i użyć jej, aby ukryć niesprawdzone ostrzeżenie.
Możesz użyć tego w następujący sposób:
Więcej dyskusji na ten temat znajduje się tutaj: http://cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html
źródło
vi
? Żartujesz?Te rzeczy są trudne, ale oto moje aktualne myśli:
Jeśli Twój interfejs API zwróci obiekt, to nic nie możesz zrobić - bez względu na wszystko, będziesz ślepo rzucał obiektem. Zezwalasz Javie na zgłaszanie wyjątków ClassCastExcept lub możesz sprawdzać każdy element osobno i zgłaszać Assertions lub IllegalArgumentExceptions lub niektóre z nich, ale wszystkie te kontrole środowiska wykonawczego są równoważne. Musisz wyłączyć czas kompilacji bez zaznaczenia rzutowania bez względu na to, co robisz w czasie wykonywania.
Wolę tylko ślepe rzutowanie i pozwolić JVM wykonać dla mnie sprawdzenie środowiska wykonawczego, ponieważ „wiemy”, co powinien zwrócić interfejs API i zazwyczaj jesteśmy gotowi założyć, że interfejs API działa. Jeśli potrzebujesz, używaj ogólnych elementów nad obsadą. Tak naprawdę nic tam nie kupujesz, ponieważ nadal masz pojedynczą obsadę w ciemno, ale przynajmniej możesz użyć odtąd ogólnych, aby JVM pomógł ci uniknąć rzucania w ciemno w innych częściach twojego kodu.
W tym konkretnym przypadku, przypuszczalnie możesz zobaczyć wezwanie do SetAttribute i zobaczyć, że typ się zbliża, więc po prostu ślepe rzucenie tego samego typu na wyjście nie jest niemoralne. Dodaj komentarz odnoszący się do SetAttribute i gotowe.
źródło
Oto skrócony przykład, który pozwala uniknąć ostrzeżenia o „niesprawdzonej obsadzie” dzięki zastosowaniu dwóch strategii wymienionych w innych odpowiedziach.
Przekaż klasę typu zainteresowania jako parametr w runtime (
Class<T> inputElementClazz
). Następnie możesz użyć:inputElementClazz.cast(anyObject);
Do rzutowania typu na kolekcję użyj symbolu wieloznacznego? zamiast ogólnego typu T, aby potwierdzić, że rzeczywiście nie wiesz, jakiego rodzaju obiektów można oczekiwać po starszym kodzie (
Collection<?> unknownTypeCollection
). W końcu to właśnie mówi nam ostrzeżenie „niezaznaczonej obsady”: nie możemy być pewni, że otrzymamyCollection<T>
, więc uczciwą rzeczą jest użycieCollection<?>
. Jeśli jest to absolutnie potrzebne, nadal można zbudować kolekcję znanego typu (Collection<T> knownTypeCollection
).Starszy kod z interfejsem w poniższym przykładzie ma atrybut „input” w StructuredViewer (StructuredViewer to widget drzewa lub tabeli, „input” to model danych za nim). To „wejście” może być dowolnym rodzajem kolekcji Java.
Oczywiście powyższy kod może powodować błędy w czasie wykonywania, jeśli użyjemy starszego kodu z niewłaściwymi typami danych (np. Jeśli ustawimy tablicę jako „wejście” StructuredViewer zamiast kolekcji Java).
Przykład wywołania metody:
źródło
W świecie sesji HTTP naprawdę nie można uniknąć przesyłania, ponieważ interfejs API jest zapisywany w ten sposób (tylko pobiera i zwraca
Object
).Przy odrobinie pracy można łatwo ominąć niesprawdzoną obsadę ”. Oznacza to, że zmieni się w tradycyjną obsadę, dając
ClassCastException
prawo do niej w przypadku błędu). Niesprawdzony wyjątek możeCCE
później zmienić się w punkt obsady (dlatego jest to osobne ostrzeżenie).Zamień HashMap na dedykowaną klasę:
Następnie rzuć na tę klasę zamiast
Map<String,String>
i wszystko zostanie sprawdzone dokładnie w miejscu, w którym napiszesz swój kod. NieoczekiwaneClassCastExceptions
później.źródło
W Android Studio, jeśli chcesz wyłączyć inspekcję, możesz użyć:
źródło
W tym konkretnym przypadku nie zapisywałbym Map bezpośrednio w HttpSession, ale zamiast tego wystąpienie mojej własnej klasy, która z kolei zawiera Mapę (szczegół implementacji klasy). Wtedy możesz być pewien, że elementy na mapie są odpowiedniego typu.
Ale jeśli mimo wszystko chcesz sprawdzić, czy zawartość mapy jest odpowiedniego typu, możesz użyć takiego kodu:
źródło
Funkcja narzędzia Objects.Unchecked w powyższej odpowiedzi autorstwa Esko Luontoli to świetny sposób na uniknięcie bałaganu w programie.
Jeśli nie chcesz, aby SuppressWarnings dotyczyła całej metody, Java zmusza cię do umieszczenia jej na lokalnym. Jeśli potrzebujesz rzutowania na członka, może to prowadzić do kodu w następujący sposób:
Korzystanie z tego narzędzia jest znacznie czystsze i nadal jest oczywiste, co robisz:
UWAGA: Uważam, że ważne jest, aby dodać, że czasami ostrzeżenie naprawdę oznacza, że robisz coś nie tak, jak:
Kompilator mówi ci, że to rzutowanie NIE zostanie sprawdzone w czasie wykonywania, więc żaden błąd środowiska wykonawczego nie zostanie zgłoszony, dopóki nie spróbujesz uzyskać dostępu do danych w ogólnym kontenerze.
źródło
Eliminacja ostrzeżeń nie jest rozwiązaniem. Nie powinieneś wykonywać rzutowania na dwa poziomy w jednym zestawieniu.
źródło
Szybkie zgadnięcie, jeśli opublikujesz swój kod, może powiedzieć na pewno, ale być może zrobiłeś coś podobnego do
która wyświetli ostrzeżenie, gdy musisz to zrobić
warto na to spojrzeć
Ogólne w języku programowania Java
jeśli nie wiesz, co należy zrobić.
źródło
Być może źle zrozumiałem pytanie (przykład i kilka otaczających linii byłoby miło), ale dlaczego nie zawsze używasz odpowiedniego interfejsu (i Java5 +)? Nie widzę powodu, dla którego kiedykolwiek chciałbyś rzucić na
HashMap
zamiast zamiastMap<KeyType,ValueType>
. W rzeczywistości nie wyobrażam sobie żadnego powodu, aby ustawić typ zmiennej naHashMap
zamiastMap
.A dlaczego źródłem jest
Object
? Czy jest to typ parametru starej kolekcji? Jeśli tak, użyj ogólnych i podaj żądany typ.źródło
Jeśli muszę użyć interfejsu API, który nie obsługuje generycznych. Próbuję izolować te wywołania w procedurach otoki z jak najmniejszą liczbą wierszy. Następnie używam adnotacji SuppressWarnings i jednocześnie dodam rzutowania dotyczące bezpieczeństwa typu.
To jest tylko osobista preferencja, aby zachować porządek.
źródło
Weźmy to, jest to znacznie szybsze niż tworzenie nowej HashMap, jeśli jest już jedna, ale nadal jest bezpieczna, ponieważ każdy element jest sprawdzany pod względem typu ...
źródło
key.isAssignableFrom(e.getKey().getClass())
można zapisać jakokey.isInstance(e.getKey())
Po prostu sprawdź to, zanim rzucisz.
A dla każdego, kto pyta, dość powszechne jest otrzymywanie obiektów, w których nie jesteś pewien tego typu. Wiele starszych implementacji „SOA” przekazuje różne obiekty, którym nie zawsze należy ufać. (Horrory!)
EDYCJA Raz zmieniłem przykładowy kod, aby pasował do aktualizacji plakatu, a po kilku komentarzach widzę, że instanceof nie działa dobrze z generycznymi. Jednak zmiana sprawdzania w celu sprawdzenia poprawności zewnętrznego obiektu wydaje się dobrze współgrać z kompilatorem wiersza poleceń. Poprawiony przykład jest teraz opublikowany.
źródło
Prawie każdy problem w informatyce można rozwiązać, dodając poziom pośredni * lub coś w tym rodzaju.
Więc wprowadź nie-ogólny obiekt, który jest na wyższym poziomie niż
Map
. Bez kontekstu nie będzie to wyglądało zbyt przekonująco, ale w każdym razie:* Z wyjątkiem zbyt wielu poziomów pośrednich.
źródło
Oto jeden ze sposobów, w jaki sobie z tym radzę, gdy zastępuję
equals()
operację.Wydaje się, że działa w Javie 8 (nawet skompilowanej
-Xlint:unchecked
)źródło
Jeśli masz pewność, że typ zwrócony przez session.getAttribute () to HashMap, nie możesz typecastować do tego dokładnego typu, ale polegaj tylko na sprawdzeniu ogólnej HashMap
Eclipse zaskoczy ostrzeżenia, ale oczywiście może to prowadzić do błędów w czasie wykonywania, które mogą być trudne do debugowania. Używam tego podejścia tylko w kontekstach niekrytycznych dla operacji.
źródło
Dwa sposoby, jeden, który całkowicie omija tag, drugi przy użyciu niegrzecznej, ale miłej metody użytkowej.
Problemem są kolekcje wstępnie uogólnione ...
Wierzę, że ogólna zasada brzmi: „rzucaj obiektami jedna rzecz naraz” - co to oznacza, gdy próbujesz użyć klas surowych w uogólnionym świecie, ponieważ nie wiesz, co znajduje się na tej Mapie <?,?> (i JVM może nawet stwierdzić, że to nawet nie jest Mapa!), to oczywiste, gdy pomyślisz o tym, że nie możesz jej rzucić. Jeśli miałeś Map <String,?> Map2, to HashSet <String> keys = (HashSet <String>) map2.keySet () nie daje ostrzeżenia, mimo że jest to „akt wiary” dla kompilatora (ponieważ może się okazać TreeSet) ... ale to tylko pojedynczy akt wiary.
PS na zastrzeżenie, że iteracja jak na mój pierwszy sposób „jest nudna” i „wymaga czasu”, odpowiedź brzmi „bez bólu nie ma zysku”: uogólniona kolekcja zawiera Map.Entry <String, String> i nic jeszcze. Musisz zapłacić za tę gwarancję. Przy systematycznym stosowaniu leków generycznych ta płatność przybiera formę kodowania zgodności, a nie czasu maszynowego!
Jedna ze szkół myśli mogłaby powiedzieć, że powinieneś ustawić ustawienia Eclipse, aby zamiast takich ostrzeżeń pojawiały się takie niesprawdzone błędy rzutowania. W takim przypadku musiałbyś skorzystać z mojej pierwszej drogi.
źródło
To powoduje, że ostrzeżenia znikają ...
źródło
Rozwiązanie: Wyłącz to ostrzeżenie w Eclipse. Nie @SuppressWarnings, po prostu całkowicie go wyłącz.
Kilka przedstawionych powyżej „rozwiązań” jest zupełnie nie na miejscu, przez co kod jest nieczytelny ze względu na głupie ostrzeżenie.
źródło
@SuppressWarnings
wcale nie czyni kodu nieczytelnym.