315 votes

Sécurité du type : Distribution non vérifiée

Dans le fichier de contexte de mon application Spring, j'ai quelque chose comme :

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

En classe java, l'implémentation ressemble à ceci :

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

Dans Eclipse, je vois un avertissement qui dit :

Sécurité de type : Transfert non vérifié d'un objet vers un HashMap

Qu'est-ce que j'ai fait de mal ? Comment puis-je résoudre le problème ?

0 votes

J'ai trouvé une routine pour vérifier le cast vers un HashMap paramétré, ce qui élimine l'avertissement de cast non vérifié : lien Je dirais que c'est la solution "correcte", mais on peut se demander si cela en vaut la peine ou non :)

0 votes

0 votes

369voto

Jon Skeet Points 692016

Le problème est qu'un cast est une vérification à l'exécution - mais en raison de l'effacement des types, à l'exécution, il n'y a en fait aucune différence entre un HashMap<String,String> y HashMap<Foo,Bar> pour tout autre Foo y Bar .

Utilisez @SuppressWarnings("unchecked") et se pincer le nez. Oh, et faites campagne pour des génériques réifiés en Java :)

19 votes

Je préfère les génériques réifiés de Java à NSMutableWhatever non typé, qui ressemble à un bond en arrière de dix ans, n'importe quel jour de la semaine. Au moins Java essaie.

13 votes

Exactement. Si vous insistez sur la vérification du type, vous ne pouvez le faire qu'avec HashMap< ?,?> et cela ne supprimera pas l'avertissement puisque cela revient à ne pas vérifier le type des types génériques. Ce n'est pas la fin du monde, mais c'est ennuyeux de devoir soit supprimer un avertissement, soit vivre avec.

13 votes

@JonSkeet C'est quoi un générique réifié ?

284voto

MetroidFan2002 Points 11413

Eh bien, tout d'abord, vous gaspillez de la mémoire avec le nouveau HashMap appel à la création. Votre deuxième ligne ignore complètement la référence à cette hashmap créée, la rendant ainsi disponible pour le ramasseur d'ordures. Donc, ne faites pas ça, utilisez :

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

Deuxièmement, le compilateur se plaint du fait que vous avez transformé l'objet en un HashMap sans vérifier s'il s'agit d'un HashMap . Mais, même si vous le faisiez :

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

Vous recevriez probablement toujours cet avertissement. Le problème est que, getBean renvoie à Object On ne sait donc pas de quel type il s'agit. En le convertissant en HashMap ne causerait pas directement le problème dans le second cas (et peut-être n'y aurait-il pas d'avertissement dans le premier cas, je ne sais pas à quel point le compilateur Java est pédant avec les avertissements pour Java 5). Cependant, vous le convertissez en un HashMap<String, String> .

Les HashMaps sont en fait des cartes qui prennent un objet comme clé et ont un objet comme valeur, HashMap<Object, Object> si vous voulez. Ainsi, il n'y a aucune garantie que lorsque vous obtiendrez votre haricot, il pourra être représenté en tant que HashMap<String, String> parce que vous auriez pu HashMap<Date, Calendar> car la représentation non générique qui est renvoyée peut comporter n'importe quel objet.

Si le code se compile, et que vous pouvez exécuter String value = map.get("thisString"); sans aucune erreur, ne vous inquiétez pas de cet avertissement. Mais si la carte n'est pas entièrement composée de clés de type chaîne de caractères et de valeurs de type chaîne de caractères, vous obtiendrez un message d'avertissement. ClassCastException au moment de l'exécution, car les génériques ne peuvent pas empêcher que cela se produise dans ce cas.

18 votes

C'était il y a un moment, mais je cherchais une réponse sur le contrôle de type d'un Set<CustomClass> avant un cast, et vous ne pouvez pas instanceof sur un générique paramétré. ex. if(event.getTarget instanceof Set<CustomClass>) Vous pouvez seulement contrôler le type d'un générique avec un ? et cela ne supprimera pas l'avertissement de cast. ex. if(event.getTarget instanceof Set<?>)

116voto

Larry Landry Points 101

Comme l'indiquent les messages ci-dessus, la Liste ne peut pas être différenciée entre une List<Object> et un List<String> o List<Integer> .

J'ai résolu ce message d'erreur pour un problème similaire :

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

avec les éléments suivants :

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

Explication : La première conversion de type vérifie que l'objet est une liste sans se soucier des types qu'elle contient (puisque nous ne pouvons pas vérifier les types internes au niveau de la liste). La deuxième conversion est maintenant nécessaire parce que le compilateur sait seulement que la liste contient une sorte d'objets. Elle vérifie le type de chaque objet de la liste lors de son accès.

5 votes

Tu as raison mon ami. Au lieu de couler la liste, il suffit de l'itérer et de couler chaque élément, l'avertissement n'apparaîtra pas, génial.

4 votes

Cela a supprimé l'avertissement mais je ne suis toujours pas sûr :P

4 votes

Oui, c'est comme si on bandait les yeux du compilateur mais pas ceux du runtime :D Je ne vois donc pas de différence entre ceci et @SuppressWarnings("unchecked").

36voto

David M. Karr Points 2210

Un avertissement n'est que cela. Un avertissement. Parfois les avertissements ne sont pas pertinents, parfois ils ne le sont pas. Ils sont utilisés pour attirer votre attention sur quelque chose que le compilateur pense être un problème, mais qui ne l'est peut-être pas.

Dans le cas des plâtres, il y aura toujours un avertissement dans ce cas. Si vous êtes absolument certain qu'un cast particulier sera sûr, alors vous devriez envisager d'ajouter une annotation comme celle-ci (je ne suis pas sûr de la syntaxe) juste avant la ligne :

@SuppressWarnings (value="unchecked")

21 votes

-1 : un avertissement ne devrait jamais être accepté. Supprimez ce genre d'avertissements ou corrigez-les. Il arrivera un moment où vous aurez trop d'avertissements et où vous ne verrez pas une seule fois ce qui est pertinent.

15 votes

Il n'est pas vraiment possible d'éviter les avertissements de cast de classe lors du casting de génériques paramétrés, par exemple Map, et c'est donc la meilleure réponse à la question initiale.

13voto

skiphoppy Points 16563

J'ai trouvé une routine pour vérifier le cast vers un HashMap paramétré, ce qui élimine l'avertissement de cast non vérifié :

lien

Je dirais que c'est la solution "correcte", mais on peut se demander si cela en vaut la peine ou non :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X