Tip güvenliği: Kontrolsüz döküm


266

İlkbahar uygulama bağlam dosyamda şöyle bir şey var:

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

Java sınıfında uygulama şöyle görünür:

private Map<String, String> someMap = new HashMap<String, String>();
someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");

Eclipse'de şöyle bir uyarı görüyorum:

Tip güvenliği: Nesneden HashMap'a işaretlenmemiş döküm

Neyi yanlış yaptım? Sorunu nasıl çözebilirim?


Ben aslında denetlenmemiş döküm uyarısı ortadan kaldırır parametreli HashMap için döküm kontrol etmek için bir rutin geldi: bağlantı Ben bu "doğru" çözüm olduğunu söyleyebilirim, ama buna değer olup olmadığını tartışmalı olabilir. :)
skiphoppy


Yanıtlar:


249

Her şeyden önce, yeni HashMapyaratım çağrısıyla hafızayı boşa harcıyorsun . İkinci satırınız, bu oluşturulan hashmap'a yapılan referansı tamamen göz ardı ederek, çöp toplayıcı tarafından kullanılabilir hale getirir. Yani, bunu yapma, kullanın:

private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");

İkincisi, derleyici, a olup HashMapolmadığını kontrol etmeden nesneyi a'ya attığınızdan şikayet ediyor HashMap. Ancak, yapsanız bile:

if(getApplicationContext().getBean("someMap") instanceof HashMap) {
    private Map<String, String> someMap = (HashMap<String, String>)getApplicationContext().getBean("someMap");
}

Muhtemelen hala bu uyarıyı alırsınız. Sorun, getBeangeri dönüyor Object, bu yüzden türün ne olduğu bilinmiyor. HashMapDoğrudan dönüştürmek ikinci durumda soruna neden olmaz (ve belki de ilk durumda bir uyarı olmaz, Java derleyicisinin Java 5 uyarılarıyla ne kadar bilgiç olduğundan emin değilim). Ancak, bunu bir HashMap<String, String>.

HashMaps gerçekten bir nesneyi anahtar olarak alan ve HashMap<Object, Object>eğer isterseniz bir nesne olarak bir değer olan haritalardır . Böylece, çekirdeğinizi aldığınızda, bunun temsil edilebileceğine dair bir garanti yoktur HashMap<String, String>, HashMap<Date, Calendar>çünkü sahip olabileceğiniz jenerik olmayan temsilin herhangi bir nesneye sahip olabileceğinden.

Kod derlenirse ve String value = map.get("thisString");hatasız çalışabiliyorsanız, bu uyarı hakkında endişelenmeyin. Ancak, harita tamamen dize değerlerine dize anahtarları değilse, bir ClassCastExceptionçalışma zamanında elde edersiniz , çünkü jenerikler bu durumda bunun olmasını engelleyemez.


12
Bu bir süre önceydi, ancak bir dökümden önce bir Set <CustomClass> kontrol tipinde bir cevap arıyordum ve parametrelenmiş bir jenerik üzerinde anlayamazsınız. ör. if (event.getTarget instanceof <CustomClass> Ayarla) Yalnızca? ve bu, döküm uyarısını kaldırmaz. eg if (event.getTarget instanceof Set <?>)
garlicman

315

Sorun şu ki, bir kadro bir çalışma zamanı kontrolüdür - ancak tür silme nedeniyle, çalışma zamanında aslında bir HashMap<String,String>ve HashMap<Foo,Bar>diğer Foove arasında hiçbir fark yoktur Bar.

@SuppressWarnings("unchecked")Burnunu kullan ve tut. Oh, ve Java'da birleşik jenerikler için kampanya :)


14
Java'nın tanınmış jeneriklerini, haftanın herhangi bir gününde on yıllık bir sıçrama gibi hissettiren türlenmemiş NSMutableWhatever'i ele alacağım. En azından Java çalışıyor.
Dan Rosenstark

12
Kesinlikle. Tip kontrolünde ısrar ederseniz, sadece HashMap <?,?> İle yapılabilir ve bu uyarı genel türleri kontrol etmemekle aynı olmadığından uyarıyı kaldırmaz. Bu dünyanın sonu değil, ya bir uyarıyı bastırırken ya da onunla birlikte yaşamakta olduğunuz için can sıkıcı bir durum.
garlicman

5
@JonSkeet Reified jenerik nedir?
SasQ

89

Yukarıdaki mesajların belirttiği gibi, Liste a List<Object>ve a List<String>veya arasında ayrım yapılamaz List<Integer>.

Benzer bir sorun için bu hata mesajını çözdüm:

List<String> strList = (List<String>) someFunction();
String s = strList.get(0);

Takip ederek:

List<?> strList = (List<?>) someFunction();
String s = (String) strList.get(0);

Açıklama: İlk tür dönüşümü, nesnenin içinde tutulan türlerle ilgilenmeden bir Liste olduğunu doğrular (çünkü dahili türleri Liste düzeyinde doğrulayamadığımız için). Derleyici yalnızca Listenin bir tür nesneler içerdiğini bildiğinden, ikinci dönüşüm gereklidir. Bu, Listedeki her nesnenin erişildiği sırada türünü doğrular.


3
haklısın arkadaşım. Listeyi yayınlamak yerine, sadece yineleyin ve her öğeyi yayınlayın, uyarı görünmeyecek, harika.
juan Isaza

2
Bu uyarıyı kaldırdı ama hala kendime
güvenmiyorum

1
Evet derleyici gözü kapalı gibi görünüyor ama çalışma zamanı değil: D Yani bu ve @SuppressWarnings ("işaretlenmemiş") arasında herhangi bir fark görmüyorum
channae

1
Bu harika! @SupressWarning kullanmanın temel farkı, ek açıklamayı kullanmanın IDE ve kod analizi araçlarınızdan gelen uyarıyı ortadan kaldırmasıdır, ancak -Werror bayrak derlemesini kullanıyorsanız, henüz hatayla karşılaşırsınız. Bu yaklaşımı kullanarak her iki uyarı da sabittir.
Edu Costa

30

Bir uyarı sadece budur. Bir uyarı. Bazen uyarılar önemsizdir, bazen de değildir. Dikkatinizi derleyicinin bir sorun olabileceğini düşündüğü, ancak olmayabileceği bir şeye çağırmak için kullanılırlar.

Atmalarda, bu durumda her zaman bir uyarı verecektir. Belirli bir oyuncu kadrosunun güvenli olacağından kesinlikle eminseniz, satırdan hemen önce böyle bir ek açıklama eklemeyi düşünmelisiniz (sözdiziminden emin değilim):

@SuppressWarnings (value="unchecked")

14
-1: bir uyarı asla kabul edilmemelidir. Veya bu tür uyarıları bastırın veya düzeltin. Çok sayıda uyarı yapmanız gereken an gelecek ve ilgili bir kez görmeyeceksiniz.
ezdazuzena

10
Parametrelendirilmiş jenerikler yani Harita oluştururken sınıf atma uyarılarından gerçekten kaçınamazsınız, bu yüzden bu orijinal soru için en iyi cevaptır.
muttonUp

9

GetBean bir Object başvurusu döndürdüğünden ve doğru türe çevirdiğiniz için bu iletiyi alıyorsunuz. Java 1.5 size bir uyarı verir. Bu şekilde çalışan kodla Java 1.5 veya daha iyisini kullanmanın doğası budur. Bahar tipik bir versiyona sahiptir

someMap=getApplicationContext().getBean<HashMap<String, String>>("someMap");

yapılacaklar listesinde.


6

Uyarılardan gerçekten kurtulmak istiyorsanız, yapabileceğiniz bir şey genel sınıftan uzanan bir sınıf oluşturmaktır.

Örneğin,

private Map<String, String> someMap = new HashMap<String, String>();

Böyle yeni bir sınıf oluşturabilirsiniz

public class StringMap extends HashMap<String, String>()
{
    // Override constructors
}

Sonra kullandığınızda

someMap = (StringMap) getApplicationContext().getBean("someMap");

Derleyici (artık genel olmayan) türlerin ne olduğunu bilir ve uyarı yapılmaz. Bu her zaman mükemmel bir çözüm olmayabilir, bazıları bu tür yenilgilerin jenerik sınıfların amacını bozduğunu iddia edebilir, ancak yine de jenerik sınıftan aynı kodu tekrar kullanıyorsunuz, derleme zamanında hangi tür kullanmak istiyorsunuz.


3

Kontrol edilmeyen uyarıyı önleme çözümü:

class MyMap extends HashMap<String, String> {};
someMap = (MyMap)getApplicationContext().getBean("someMap");

Bir çözüm değil kesmek gibi görünüyor.
Malwinder Singh

1
- Serileştirilebilir sınıf MyMap, long türünde bir statik son serialVersionUID alanı bildirmez: {
Ulterior

1

Başka bir çözüm, kendinizi aynı nesneyi çok fazla döküm yaparken bulursanız ve kodunuzu değiştirmek istemiyorsanız @SupressWarnings("unchecked"), ek açıklama ile bir yöntem oluşturmak olacaktır. Bu şekilde oyuncu kadrosunu merkezileştirirsiniz ve umarım hata olasılığını azaltırsınız.

@SuppressWarnings("unchecked")
public static List<String> getFooStrings(Map<String, List<String>> ctx) {
    return (List<String>) ctx.get("foos");
}

1

Aşağıdaki kod nedenleri Tür güvenliği Uyarı

Map<String, Object> myInput = (Map<String, Object>) myRequest.get();

Geçici çözüm

Listede tutulan nesne türü doğrulanmadığından parametrelerden bahsetmeden yeni bir Harita Nesnesi oluşturun.

1. Adım: Yeni bir geçici Harita oluşturun

Map<?, ?> tempMap = (Map<?, ?>) myRequest.get();

2. Adım: Ana Haritayı Örnekleme

Map<String, Object> myInput=new HashMap<>(myInputObj.size());

3. Adım: Geçici Haritayı yineleyin ve değerleri ana Haritaya ayarlayın

 for(Map.Entry<?, ?> entry :myInputObj.entrySet()){
        myInput.put((String)entry.getKey(),entry.getValue()); 
    }

0

Neyi yanlış yaptım? Sorunu nasıl çözebilirim?

Buraya :

Map<String,String> someMap = (Map<String,String>)getApplicationContext().getBean("someMap");

Genellikle döndürdüğümüz için kullanmak istemediğimiz eski bir yöntem kullanırsınız Object:

Object getBean(String name) throws BeansException;

Fasulye fabrikasından bir fasulye elde etmeyi (prototip için) (tekton için) tercih etme yöntemi:

<T> T getBean(String name, Class<T> requiredType) throws BeansException;

Gibi kullanma:

Map<String,String> someMap = app.getBean(Map.class,"someMap");

derlenecek, ancak yine de denetlenmeyen bir dönüşüm uyarısıyla tüm Mapnesneler zorunlu olarak Map<String, String>nesne olmadığından .

Ancak <T> T getBean(String name, Class<T> requiredType) throws BeansException;genel koleksiyonlar gibi fasulye jenerik sınıflarında yeterli değildir, çünkü parametre olarak birden fazla sınıf belirtmek gerekir: toplama türü ve genel türü (türleri).

Bu tür bir senaryoda ve genel olarak, daha iyi bir yaklaşım doğrudan BeanFactoryyöntemleri kullanmak değil , çerçevenin fasulyeyi enjekte etmesine izin vermektir.

Fasulye bildirimi:

@Configuration
public class MyConfiguration{

    @Bean
    public Map<String, String> someMap() {
        Map<String, String> someMap = new HashMap();
        someMap.put("some_key", "some value");
        someMap.put("some_key_2", "some value");
        return someMap;
    }
}

Fasulye enjeksiyonu:

@Autowired
@Qualifier("someMap")
Map<String, String> someMap;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.