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?
java
spring
type-safety
unchecked
Zrodzony ze smoka
źródło
źródło
Odpowiedzi:
Po pierwsze, marnujesz pamięć dzięki nowemu
HashMap
wezwaniu 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:Po drugie, kompilator narzeka, że rzuciłeś obiekt na obiekt
HashMap
bez sprawdzania, czy jest to obiektHashMap
. Ale nawet jeśli miałbyś zrobić:Prawdopodobnie nadal dostaniesz to ostrzeżenie. Problemem jest
getBean
powrótObject
, więc nie wiadomo, jaki jest typ. Przekształcenie go w trybHashMap
bezpośredni nie spowodowałoby problemu z drugim przypadkiem (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). Konwertujesz go jednak naHashMap<String, String>
.HashMaps to tak naprawdę mapy, które przyjmują obiekt jako klucz i mają obiekt jako wartość,
HashMap<Object, Object>
jeśli wolisz. Dlatego nie ma gwarancji, że po otrzymaniu fasoli może ona być reprezentowana jako,HashMap<String, String>
ponieważ można ją uzyskać,HashMap<Date, Calendar>
ponieważ zwracana reprezentacja nieogólna może zawierać dowolne obiekty.Jeśli kod się skompiluje i możesz wykonać
String value = map.get("thisString");
bez 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, pojawi sięClassCastException
w czasie wykonywania, ponieważ ogólne nie mogą tego zablokować.źródło
Problem polega na tym, że rzutowanie jest sprawdzaniem środowiska wykonawczego - ale ze względu na kasowanie typu, w czasie wykonywania nie ma właściwie różnicy między a
HashMap<String,String>
aHashMap<Foo,Bar>
dla innychFoo
iBar
.Użyj
@SuppressWarnings("unchecked")
i przytrzymaj nos. Aha, i kampania dla zreifikowanych generyków w Javie :)źródło
Jak wskazują powyższe komunikaty, listy nie można rozróżnić między a
List<Object>
i aList<String>
lubList<Integer>
.Rozwiązałem ten komunikat o błędzie dotyczący podobnego problemu:
z następującymi:
Objaś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.
źródło
Ostrzeżenie jest właśnie takie. Ostrzeżenie. Czasami ostrzeżenia są nieistotne, a czasem nie. Służą do zwrócenia uwagi na coś, co kompilator uważa za problem, ale może nie być.
W przypadku rzutów zawsze będzie to ostrzeżenie. 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:
źródło
Otrzymujesz ten komunikat, ponieważ getBean zwraca odwołanie do obiektu i rzutujesz go na poprawny typ. Java 1.5 wyświetla ostrzeżenie. Taka jest natura używania Java 1.5 lub lepszej z kodem, który działa w ten sposób. Wiosna ma bezpieczną wersję
na liście rzeczy do zrobienia.
źródło
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ć
Możesz stworzyć nową taką klasę
Następnie, kiedy używasz
Kompilator NIE wie, jakie są typy (już nie ogólne) i nie będzie ostrzeżenia. Nie zawsze może to być idealne rozwiązanie, niektórzy mogą argumentować, że tego rodzaju porażka jest celem klas ogólnych, ale nadal używasz całego tego samego kodu z klasy ogólnej, po prostu deklarujesz w czasie kompilacji, jakiego typu chcesz użyć.
źródło
Rozwiązanie pozwalające uniknąć niesprawdzonego ostrzeżenia:
źródło
Inne rozwiązanie, jeśli często rzucasz ten sam obiekt i nie chcesz zaśmiecać swojego kodu
@SupressWarnings("unchecked")
, byłoby stworzenie metody z adnotacją. W ten sposób scentralizujesz obsadę i, mam nadzieję, zmniejszysz prawdopodobieństwo błędu.źródło
Poniższy kod powoduje Ostrzeżenie o bezpieczeństwie typu
Map<String, Object> myInput = (Map<String, Object>) myRequest.get();
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ę
Krok 3: Powtórz tymczasową mapę i ustaw wartości w głównej mapie
źródło
Tutaj:
Map<String,String> someMap = (Map<String,String>)getApplicationContext().getBean("someMap");
Używasz starszej metody, której zazwyczaj nie chcemy używać, ponieważ zwraca
Object
:Metodą preferowania uzyskania (dla singletona) / utworzenia (dla prototypu) fasoli z fabryki fasoli jest:
Używając go, takiego jak:
będzie się kompilować, ale nadal z nie zaznaczonym ostrzeżeniem o konwersji, ponieważ wszystkie
Map
obiekty niekoniecznie sąMap<String, String>
obiektami.Ale
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
to nie wystarczy w klasach ogólnych komponentu bean, takich jak kolekcje ogólne, ponieważ wymaga to podania więcej niż jednej klasy jako parametru: typu kolekcji i jej ogólnych typów.W tego rodzaju scenariuszu i ogólnie lepszym podejściem nie jest stosowanie
BeanFactory
metod bezpośrednich , ale umożliwienie ramce wstrzyknięcia fasoli.Deklaracja fasoli:
Wstrzyknięcie fasoli:
źródło