it-swarm.com.de

Typensicherheit: Ungeprüfte Besetzung

In meiner Anwendungskontextdatei für den Frühling habe ich Folgendes:

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

In Java class sieht die Implementierung folgendermaßen aus:

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

In Eclipse sehe ich eine Warnung, die besagt:

Typensicherheit: Deaktivierte Umwandlung von Object in HashMap

Was habe ich falsch gemacht? Wie löse ich das Problem?

239
DragonBorn

Zunächst verschwenden Sie Speicherplatz mit dem neuen Erstellungsaufruf HashMap. Ihre zweite Zeile ignoriert den Verweis auf diese erstellte Hashmap vollständig und macht sie dann für den Garbage Collector verfügbar. Also tu das nicht, benutze:

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

Zweitens beschwert sich der Compiler, dass Sie das Objekt in ein HashMap umwandeln, ohne zu prüfen, ob es sich um ein HashMap handelt. Aber selbst wenn Sie tun würden:

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

Sie würden wahrscheinlich immer noch diese Warnung erhalten. Das Problem ist, dass getBeanObject zurückgibt, sodass der Typ unbekannt ist. Das direkte Konvertieren in HashMap würde das Problem mit dem zweiten Fall nicht verursachen (und möglicherweise würde es im ersten Fall keine Warnung geben, ich bin nicht sicher, wie umständlich das Java = Compiler ist mit Warnungen für Java 5). Sie konvertieren es jedoch in einen HashMap<String, String>.

HashMaps sind wirklich Maps, die ein Objekt als Schlüssel und ein Objekt als Wert haben, HashMap<Object, Object> Wenn Sie so wollen. Daher gibt es keine Garantie dafür, dass Ihre Bean beim Abrufen als HashMap<String, String> Dargestellt werden kann, da Sie möglicherweise HashMap<Date, Calendar> Haben, da die zurückgegebene nicht generische Darstellung beliebige Objekte enthalten kann.

Wenn der Code kompiliert wird und Sie String value = map.get("thisString"); ohne Fehler ausführen können, sorgen Sie sich nicht um diese Warnung. Wenn die Zuordnung jedoch nicht vollständig aus Zeichenfolgenschlüsseln für Zeichenfolgenwerte besteht, erhalten Sie zur Laufzeit ein ClassCastException, da die Generics dies in diesem Fall nicht blockieren können.

236
MetroidFan2002

Das Problem ist, dass ein Cast eine Laufzeitüberprüfung ist - aber aufgrund der Typlöschung gibt es zur Laufzeit keinen Unterschied zwischen HashMap<String,String> Und HashMap<Foo,Bar> Für alle anderen Foo und Bar.

Benutze @SuppressWarnings("unchecked") und halte deine Nase. Oh, und Kampagne für wiedervereinigte Generika in Java :)

293
Jon Skeet

Wie die obigen Meldungen zeigen, kann die Liste nicht zwischen einem List<Object> und ein List<String> oder List<Integer>.

Ich habe diese Fehlermeldung für ein ähnliches Problem gelöst:

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

mit den folgenden:

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

Erläuterung: Bei der ersten Typkonvertierung wird überprüft, ob es sich bei dem Objekt um eine Liste handelt, ohne auf die darin enthaltenen Typen zu achten (da die internen Typen auf Listenebene nicht überprüft werden können). Die zweite Konvertierung ist jetzt erforderlich, da der Compiler nur weiß, dass die Liste eine Art von Objekten enthält. Dadurch wird der Typ jedes Objekts in der Liste beim Zugriff überprüft.

77
Larry Landry

Eine Warnung ist genau das. Eine Warnung. Manchmal sind Warnungen irrelevant, manchmal nicht. Sie werden verwendet, um Ihre Aufmerksamkeit auf etwas zu lenken, von dem der Compiler glaubt, dass es ein Problem sein könnte, dies aber möglicherweise nicht.

Bei Abgüssen wird in diesem Fall immer eine Warnung ausgegeben. Wenn Sie absolut sicher sind, dass eine bestimmte Besetzung sicher ist, sollten Sie eine Annotation wie diese (ich bin mir der Syntax nicht sicher) direkt vor der Zeile hinzufügen:

@SuppressWarnings (value="unchecked")
27
David M. Karr

Sie erhalten diese Meldung, weil getBean eine Objektreferenz zurückgibt und Sie sie in den richtigen Typ umwandeln. Java 1.5 gibt Ihnen eine Warnung. Das ist die Art der Verwendung von Java 1.5 oder besser mit Code, der so funktioniert. Spring hat die typsichere Version

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

auf seiner ToDo-Liste.

9
David Nehme

Wenn Sie die Warnungen wirklich loswerden möchten, können Sie eine Klasse erstellen, die von der generischen Klasse ausgeht.

Zum Beispiel, wenn Sie versuchen, zu verwenden

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

Sie können eine neue Klasse wie diese erstellen

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

Dann, wenn Sie verwenden

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

Der Compiler weiß, was die (nicht mehr generischen) Typen sind, und es wird keine Warnung angezeigt. Dies ist möglicherweise nicht immer die perfekte Lösung. Einige argumentieren möglicherweise, dass diese Art von Niederlagen den Zweck von generischen Klassen widerspiegelt. Sie verwenden jedoch weiterhin denselben Code aus der generischen Klasse. Sie legen lediglich beim Kompilieren fest, welcher Typ verwendet werden soll du willst benutzen.

5
Rabbit

Die Lösung, um die ungeprüfte Warnung zu vermeiden:

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

Eine andere Lösung, wenn Sie feststellen, dass Sie dasselbe Objekt häufig verwenden und Ihren Code nicht mit @SupressWarnings("unchecked") verunreinigen möchten, besteht darin, eine Methode mit der Annotation zu erstellen. Auf diese Weise zentralisieren Sie die Besetzung und reduzieren hoffentlich die Möglichkeit von Fehlern.

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

Der folgende Code verursacht eine Sicherheitswarnung

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

Problemumgehung

Erstellen Sie ein neues Kartenobjekt, ohne die Parameter zu erwähnen, da der in der Liste enthaltene Objekttyp nicht überprüft wird.

Schritt 1: Erstellen Sie eine neue temporäre Karte

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

Schritt 2: Instanziieren Sie die Hauptkarte

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

Schritt 3: Durchlaufen Sie die temporäre Karte und setzen Sie die Werte in die Hauptkarte

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

Was habe ich falsch gemacht? Wie löse ich das Problem?

Hier :

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

Sie verwenden eine Legacy-Methode, die wir im Allgemeinen nicht verwenden möchten, da dies Object zurückgibt:

Object getBean(String name) throws BeansException;

Die bevorzugte Methode, um (für Singleton)/(für Prototypen) eine Bohne aus einer Bohnenfabrik zu erstellen, ist:

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

Verwenden Sie es wie:

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

kompiliert aber immer noch mit einer ungeprüften Konvertierungswarnung, da alle Map -Objekte nicht unbedingt Map<String, String> - Objekte sind.

Aber <T> T getBean(String name, Class<T> requiredType) throws BeansException; ist in generischen Bean-Klassen wie generischen Auflistungen nicht ausreichend, da dafür mehr als eine Klasse als Parameter angegeben werden muss: der Auflistungstyp und seine generischen Typen.

In dieser Art von Szenario und im Allgemeinen besteht ein besserer Ansatz darin, nicht direkt BeanFactory -Methoden zu verwenden, sondern das Framework die Bean injizieren zu lassen.

Die Bohnenerklärung:

@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;
    }
}

Die Bohnenspritze:

@Autowired
@Qualifier("someMap")
Map<String, String> someMap;
0
davidxxx