Domanda

Nel mio file di contesto dell'applicazione primavera, ho qualcosa del tipo:

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

Nella classe java, l'implementazione è simile a:

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

In Eclipse, vedo un avviso che dice:

Sicurezza del tipo: cast non selezionato da Object a HashMap

Cosa ho fatto di sbagliato? Come posso risolvere il problema?

È stato utile?

Soluzione

Bene, prima di tutto, stai sprecando memoria con la nuova HashMap chiamata di creazione. La seconda riga ignora completamente il riferimento a questa hashmap creata, rendendola quindi disponibile per il Garbage Collector. Quindi, non farlo, usa:

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

In secondo luogo, il compilatore si lamenta di aver lanciato l'oggetto su getBean senza verificare se si tratta di Object. Ma, anche se dovessi farlo:

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

Probabilmente riceveresti ancora questo avviso. Il problema è che HashMap<String, String> restituisce HashMap<Object, Object>, quindi non si sa quale sia il tipo. La conversione diretta in HashMap<Date, Calendar> non causerebbe il problema con il secondo caso (e forse non ci sarebbe un avviso nel primo caso, non sono sicuro di quanto sia pedante il compilatore Java con avvisi per Java 5). Tuttavia, lo stai convertendo in String value = map.get("thisString");.

Le hashmap sono in realtà mappe che prendono un oggetto come chiave e hanno un oggetto come valore, ClassCastException se vuoi. Pertanto, non vi è alcuna garanzia che quando si ottiene il bean che può essere rappresentato come <=> perché è possibile avere <=> perché la rappresentazione non generica restituita può contenere qualsiasi oggetto.

Se il codice viene compilato e puoi eseguire <=> senza errori, non preoccuparti per questo avviso. Ma se la mappa non è completamente composta da chiavi di stringa e valori di stringa, otterrai <=> in fase di esecuzione, poiché i generici non possono impedire che ciò accada in questo caso.

Altri suggerimenti

Il problema è che un cast è un controllo di runtime - ma a causa della cancellazione del tipo, in fase di runtime non c'è in realtà alcuna differenza tra HashMap<String,String> e HashMap<Foo,Bar> per qualsiasi altro Foo e Bar.

Usa @SuppressWarnings("unchecked") e tieni il naso. Oh, e campagna per generici reificati in Java :)

Come indicato dai messaggi sopra, l'elenco non può essere differenziato tra un List<Object> e un List<String> o List<Integer>.

Ho risolto questo messaggio di errore per un problema simile:

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

con il seguente:

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

Spiegazione: La prima conversione del tipo verifica che l'oggetto sia un Elenco senza preoccuparsi dei tipi contenuti all'interno (poiché non possiamo verificare i tipi interni a livello Elenco). La seconda conversione è ora richiesta perché il compilatore conosce solo che l'elenco contiene una sorta di oggetti. Ciò verifica il tipo di ciascun oggetto nell'elenco mentre si accede.

Un avvertimento è proprio questo. Un avvertimento. A volte gli avvisi sono irrilevanti, a volte no. Sono abituati a richiamare la tua attenzione su qualcosa che il compilatore ritiene possa essere un problema, ma potrebbe non esserlo.

Nel caso dei cast, in questo caso fornirà sempre un avviso. Se sei assolutamente certo che un determinato cast sarà sicuro, allora dovresti considerare di aggiungere un'annotazione come questa (non sono sicuro della sintassi) appena prima della riga:

@SuppressWarnings (value="unchecked")

Stai ricevendo questo messaggio perché getBean restituisce un riferimento a un oggetto e lo stai lanciando nel tipo corretto. Java 1.5 ti dà un avvertimento. Questa è la natura dell'uso di Java 1.5 o superiore con un codice che funziona in questo modo. Spring ha la versione typesafe

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

nella sua lista delle cose da fare.

Se vuoi davvero sbarazzarti degli avvertimenti, una cosa che puoi fare è creare una classe che si estenda dalla classe generica.

Ad esempio, se stai cercando di utilizzare

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

Puoi creare una nuova classe come tale

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

Quindi quando si utilizza

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

Il compilatore conosce quali sono i tipi (non più generici) e non ci saranno avvisi. Questa potrebbe non essere sempre la soluzione perfetta, alcuni potrebbero obiettare che questo tipo di sconfigge lo scopo delle classi generiche, ma stai ancora riutilizzando tutto lo stesso codice della classe generica, stai solo dichiarando in fase di compilazione quale tipo vuoi usare.

Un'altra soluzione, se ti ritrovi a lanciare molto lo stesso oggetto e non vuoi sporcare il tuo codice con @SupressWarnings("unchecked"), sarebbe creare un metodo con l'annotazione. In questo modo stai centralizzando il cast e, si spera, riduci la possibilità di errore.

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

Il codice seguente indica il tipo Avviso di sicurezza

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

  

Soluzione

Crea un nuovo oggetto mappa senza menzionare i parametri perché il tipo di oggetto contenuto nell'elenco non è verificato.

Passaggio 1: crea una nuova mappa temporanea

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

Passaggio 2: crea un'istanza della mappa principale

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

Passaggio 3: modifica la mappa temporanea e imposta i valori nella mappa principale

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

La soluzione per evitare l'avvertenza non selezionata:

class MyMap extends HashMap<String, String> {};
someMap = (MyMap)getApplicationContext().getBean("someMap");
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top