/ / Bezpieczeństwo typu: niezaznaczone obsada - java, sprężyna, bezpieczeństwo typu, niezaznaczone

Bezpieczeństwo typu: niesprawdzona obsada - java, wiosna, bezpieczeństwo typu, niezaznaczone

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ź № 1

Po 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());
}