W moim pliku kontekstowym aplikacji wiosennej mam coś takiego:
<util:map id="someMap" map-class="java.util.HashMap" key-type="java.lang.String" value-type="java.lang.String">
<entry key="some_key" value="some value" />
<entry key="some_key_2" value="some value" />
</util:map>
W klasie java implementacja wygląda następująco:
private Map<String, String> someMap = new HashMap<String, String>();
someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");
W Eclipse widzę ostrzeżenie:
Bezpieczeństwo typu: Niezaznaczone rzutowanie z Object na HashMap
Co zrobiłem źle? Jak rozwiązać problem?
Odpowiedzi:
218 za odpowiedź № 1Po pierwsze, marnujesz pamięć dzięki nowemu HashMap
wezwanie do stworzenia. Druga linia całkowicie pomija odniesienie do utworzonej mapy skrótów, dzięki czemu jest ona dostępna dla śmieciarza. Więc nie rób tego, użyj:
private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");
Po drugie, kompilator narzeka, że rzutowałeś obiekt na a HashMap
bez sprawdzania, czy jest to HashMap
. Ale nawet jeśli miałbyś zrobić:
if(getApplicationContext().getBean("someMap") instanceof HashMap) {
private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");
}
Prawdopodobnie nadal dostaniesz to ostrzeżenie. Problemem jest, getBean
zwraca Object
, więc nie wiadomo, jaki jest typ. Konwertowanie go na HashMap
bezpośrednio nie spowodowałoby problemu zdrugi przypadek (i być może nie byłoby ostrzeżenia w pierwszym przypadku, nie jestem pewien, jak pedantyczny jest kompilator Java z ostrzeżeniami dla Java 5). Jednak konwertujesz go na HashMap<String, String>
.
HashMaps to tak naprawdę mapy, które biorą obiekt za klucz i mają obiekt jako wartość, HashMap<Object, Object>
Jeśli będziesz. Dlatego nie ma gwarancji, że po otrzymaniu fasoli można ją przedstawić jako HashMap<String, String>
ponieważ mogłeś HashMap<Date, Calendar>
ponieważ zwracana reprezentacja nieogólna może mieć dowolne obiekty.
Jeśli kod się skompiluje i można wykonać String value = map.get("thisString");
bez żadnych błędów, nie przejmuj się tym ostrzeżeniem. Ale jeśli mapa nie zawiera w pełni kluczy łańcuchowych do wartości łańcuchowych, otrzymasz ClassCastException
w czasie wykonywania, ponieważ ogólne nie mogą tego zablokować w tym przypadku.
263 na odpowiedź nr 2
Problem polega na tym, że rzutowanie jest sprawdzaniem środowiska wykonawczego - ale z powodu usunięcia typu, w czasie wykonywania nie ma właściwie różnicy między HashMap<String,String>
i HashMap<Foo,Bar>
dla każdego innego Foo
i Bar
.
Posługiwać się @SuppressWarnings("unchecked")
i trzymaj nos. Aha, i kampania dla zreifikowanych generycznych w Javie :)
59 dla odpowiedzi nr 3
Jak wskazują powyższe komunikaty, listy nie można rozróżnić między a List<Object>
i a List<String>
lub List<Integer>
.
Rozwiązałem ten komunikat o błędzie dla podobnego problemu:
List<String> strList = (List<String>) someFunction();
String s = strList.get(0);
z następującymi:
List<?> strList = (List<?>) someFunction();
String s = (String) strList.get(0);
Wyjaśnienie: Pierwsza konwersja typu sprawdza, czy obiekt jest Listą, nie dbając o typy przechowywane w nim (ponieważ nie możemy zweryfikować typów wewnętrznych na poziomie Listy). Druga konwersja jest teraz wymagana, ponieważ kompilator wie tylko, że Lista zawiera jakieś obiekty. To weryfikuje typ każdego obiektu na liście, gdy jest on dostępny.
22 na odpowiedź № 4
Ostrzeżenie jest właśnie takie. Ostrzeżenie. Czasami ostrzeżenia są nieistotne, a czasem „nie. Są one używane do zwrócenia uwagi na coś, co zdaniem kompilatora może stanowić problem, ale nie musi.
W przypadku rzutów zawsze daostrzeżenie w tym przypadku. Jeśli masz absolutną pewność, że konkretna obsada będzie bezpieczna, powinieneś rozważyć dodanie takiej adnotacji (nie jestem pewien co do składni) tuż przed wierszem:
@SuppressWarnings (value="unchecked")
9 dla odpowiedzi № 5
Otrzymujesz tę wiadomość, ponieważ getBeanzwraca odwołanie do obiektu i rzutujesz go na poprawny typ. Java 1.5 wyświetla ostrzeżenie. Taka jest natura korzystania z Java 1.5 lub lepszej z kodem, który działa w ten sposób. Spring ma wersję typafe
someMap=getApplicationContext().getBean<HashMap<String, String>>("someMap");
na liście rzeczy do zrobienia.
5 dla odpowiedzi № 6
Jeśli naprawdę chcesz pozbyć się ostrzeżeń, jedną rzeczą, którą możesz zrobić, to stworzyć klasę, która wywodzi się z klasy ogólnej.
Na przykład, jeśli próbujesz użyć
private Map<String, String> someMap = new HashMap<String, String>();
Możesz stworzyć nową taką klasę
public class StringMap extends HashMap<String, String>()
{
// Override constructors
}
Następnie, kiedy używasz
someMap = (StringMap) getApplicationContext().getBean("someMap");
Kompilator NIE wie, co (jużogólne) są, i nie będzie ostrzeżenia. Nie zawsze może to być idealne rozwiązanie, niektórzy mogą argumentować, że tego rodzaju porażka nie jest celem klas ogólnych, ale wciąż „ponownie używasz całego tego samego kodu z klasy ogólnej, po prostu deklarujesz w czasie kompilacji jaki typ chcesz użyć.
1 dla odpowiedzi № 7
Inne rozwiązanie, jeśli często rzucasz ten sam obiekt i nie chcesz zaśmiecać swojego kodu @SupressWarnings("unchecked")
, byłoby utworzenie metody z adnotacją. W ten sposób „scentralizujesz obsadę i, mam nadzieję, zmniejszysz prawdopodobieństwo błędu.
@SuppressWarnings("unchecked")
public static List<String> getFooStrings(Map<String, List<String>> ctx) {
return (List<String>) ctx.get("foos");
}
0 dla odpowiedzi № 8
Poniższy kod powoduje Ostrzeżenie o bezpieczeństwie typu
Map<String, Object> myInput = (Map<String, Object>) myRequest.get();
Obejście
Utwórz nowy obiekt mapy bez podawania parametrów, ponieważ typ obiektu znajdującego się na liście nie jest weryfikowany.
Krok 1: Utwórz nową tymczasową mapę
Map<?, ?> tempMap = (Map<?, ?>) myRequest.get();
Krok 2: Utwórz główną mapę
Map<String, Object> myInput=new HashMap<>(myInputObj.size());
Krok 3: Iteruj tymczasową mapę i ustaw wartości w głównej mapie
for(Map.Entry<?, ?> entry :myInputObj.entrySet()){
myInput.put((String)entry.getKey(),entry.getValue());
}