Eclipse me donne un avertissement du formulaire suivant:
Sécurité du type: conversion non contrôlée de l'objet vers HashMap
C'est à partir d'un appel à une API que je n'ai aucun contrôle sur lequel retourne Object:
HashMap<String, String> getItems(javax.servlet.http.HttpSession session) {
HashMap<String, String> theHash = (HashMap<String, String>)session.getAttribute("attributeKey");
return theHash;
}
J'aimerais éviter les avertissements Eclipse, si possible, car ils indiquent théoriquement au moins un problème de code potentiel. Cependant, je n'ai pas encore trouvé un bon moyen d'éliminer celui-ci. Je peux extraire la seule ligne impliquée vers une méthode par elle-même et ajouter @SuppressWarnings("unchecked")
à cette méthode, limitant ainsi l'impact d'avoir un bloc de code où j'ignore les avertissements. De meilleures options? Je ne veux pas désactiver ces avertissements dans Eclipse.
Avant d'arriver au code, il était plus simple, mais provoquait toujours des avertissements:
HashMap getItems(javax.servlet.http.HttpSession session) {
HashMap theHash = (HashMap)session.getAttribute("attributeKey");
return theHash;
}
Le problème était ailleurs lorsque vous essayez d'utiliser le hachage, vous obtenez des avertissements:
HashMap items = getItems(session);
items.put("this", "that");
Type safety: The method put(Object, Object) belongs to the raw type HashMap. References to generic type HashMap<K,V> should be parameterized.
enum
ou même des instances deClass<T>
), afin que vous puissiez la regarder et savoir qu'elle est sûre.Réponses:
La réponse évidente, bien sûr, n'est pas de faire le casting non contrôlé.
Si c'est absolument nécessaire, essayez au moins de limiter la portée de l'
@SuppressWarnings
annotation. Selon ses Javadocs , il peut aller sur des variables locales; de cette façon, cela n'affecte même pas la méthode entière.Exemple:
Il n'y a aucun moyen de déterminer si le
Map
devrait vraiment avoir les paramètres génériques<String, String>
. Vous devez savoir à l'avance quels devraient être les paramètres (ou vous découvrirez quand vous en aurez unClassCastException
). C'est pourquoi le code génère un avertissement, car le compilateur ne peut pas savoir s'il est sûr.la source
String s = (String) new Object() ;
ne reçoit aucun avertissement, même si le compilateur ne sait pas que la conversion est sûre. L'avertissement est dû au fait que le compilateur (a) ne sait pas que le transtypage est sûr ET (b) ne générera pas une vérification complète de l'exécution au moment du transtypage. Il y aura une vérification que c'est unHashmap
, mais il n'y aura pas de vérification que c'est unHashMap<String,String>
.Malheureusement, il n'y a pas de grandes options ici. N'oubliez pas que le but de tout cela est de préserver la sécurité des types. " Java Generics " offre une solution pour gérer les bibliothèques héritées non génériques, et il en existe une en particulier appelée "technique de boucle vide" dans la section 8.2. Fondamentalement, effectuez la conversion non sécurisée et supprimez l'avertissement. Parcourez ensuite la carte comme ceci:
Si un type inattendu est rencontré, vous obtiendrez un runtime
ClassCastException
, mais au moins cela se produira près de la source du problème.la source
Sensationnel; Je pense avoir trouvé la réponse à ma propre question. Je ne suis tout simplement pas sûr que ça en vaille la peine! :)
Le problème est que la distribution n'est pas vérifiée. Donc, vous devez le vérifier vous-même. Vous ne pouvez pas simplement vérifier un type paramétré avec instanceof, car les informations de type paramétré ne sont pas disponibles au moment de l'exécution, après avoir été effacées au moment de la compilation.
Mais, vous pouvez effectuer une vérification sur chaque élément du hachage, avec instanceof, et ce faisant, vous pouvez construire un nouveau hachage qui est de type sécurisé. Et vous ne provoquerez aucun avertissement.
Grâce à mmyers et Esko Luontola, j'ai paramétré le code que j'ai écrit ici à l'origine, afin qu'il puisse être emballé dans une classe utilitaire quelque part et utilisé pour n'importe quel HashMap paramétré. Si vous voulez mieux le comprendre et que vous n'êtes pas très familier avec les génériques, j'encourage à consulter l'historique des modifications de cette réponse.
C'est beaucoup de travail, peut-être pour très peu de récompense ... Je ne sais pas si je vais l'utiliser ou non. J'apprécierais tout commentaire pour savoir si les gens pensent que cela en vaut la peine ou non. Aussi, j'apprécierais les suggestions d'amélioration: y a-t-il autre chose que je peux faire en plus de lancer AssertionErrors? Y a-t-il quelque chose de mieux que je pourrais lancer? Dois-je en faire une exception vérifiée?
la source
Dans les préférences Eclipse, accédez à Java-> Compilateur-> Erreurs / Avertissements-> Types génériques et cochez la
Ignore unavoidable generic type problems
case.Cela répond à l'intention de la question, à savoir
sinon l'esprit.
la source
uses unchecked or unsafe operations.
erreur " "javac
, mais l'ajout a@SuppressWarnings("unchecked")
rendu Eclipse mécontent, affirmant que la suppression n'était pas nécessaire. Décocher cette case fait Eclipse etjavac
se comporte de la même manière, ce que je voulais. La suppression explicite de l'avertissement dans le code est beaucoup plus claire que sa suppression partout dans Eclipse.Vous pouvez créer une classe d'utilitaires comme celle-ci et l'utiliser pour supprimer l'avertissement non contrôlé.
Vous pouvez l'utiliser comme suit:
Plus de discussion à ce sujet est ici: http://cleveralias.blogs.com/thought_spearmints/2006/01/suppresswarning.html
la source
vi
? Est-ce que vous plaisantez?Ce truc est difficile, mais voici mes pensées actuelles:
Si votre API renvoie un objet, il n'y a rien que vous puissiez faire - quoi qu'il arrive, vous lancerez aveuglément l'objet. Vous laissez Java lever des ClassCastExceptions, ou vous pouvez vérifier vous-même chaque élément et lever des Assertions ou IllegalArgumentExceptions ou quelques-unes de ces fonctions, mais ces vérifications d' exécution sont toutes équivalentes. Vous devez supprimer le temps de compilation non contrôlé, peu importe ce que vous faites au moment de l'exécution.
Je préfère simplement effectuer une conversion aveugle et laisser la JVM effectuer sa vérification d'exécution pour moi car nous "savons" ce que l'API doit retourner, et nous sommes généralement prêts à supposer que l'API fonctionne. Utilisez des génériques partout au-dessus de la distribution, si vous en avez besoin. Vous n'achetez vraiment rien là-bas car vous avez toujours le casting aveugle unique, mais au moins vous pouvez utiliser des génériques à partir de là pour que la JVM puisse vous aider à éviter les lancers aveugles dans d'autres parties de votre code.
Dans ce cas particulier, on peut supposer que vous pouvez voir l'appel à SetAttribute et voir que le type va entrer, donc il n'est pas immoral de simplement lancer le type à l'aveugle sur le même type à la sortie. Ajoutez un commentaire référençant le SetAttribute et terminez avec.
la source
Voici un exemple abrégé qui évite l'avertissement "distribution non contrôlée" en utilisant deux stratégies mentionnées dans d'autres réponses.
Transmettez la classe du type d'intérêt en tant que paramètre lors de l'exécution (
Class<T> inputElementClazz
). Ensuite, vous pouvez utiliser:inputElementClazz.cast(anyObject);
Pour le casting de type d'une collection, utilisez le caractère générique? au lieu d'un type générique T pour reconnaître que vous ne savez en effet pas quel type d'objets attendre du code hérité (
Collection<?> unknownTypeCollection
). Après tout, c'est ce que l'avertissement "distribution non contrôlée" veut nous dire: nous ne pouvons pas être sûrs d'obtenir unCollection<T>
, donc la chose honnête à faire est d'utiliser unCollection<?>
. Si absolument nécessaire, une collection d'un type connu peut toujours être construite (Collection<T> knownTypeCollection
).Le code hérité interfacé dans l'exemple ci-dessous a un attribut «entrée» dans StructuredViewer (StructuredViewer est un widget d'arborescence ou de table, «entrée» est le modèle de données derrière). Cette "entrée" pourrait être n'importe quel type de collection Java.
Naturellement, le code ci-dessus peut donner des erreurs d'exécution si nous utilisons le code hérité avec les mauvais types de données (par exemple, si nous définissons un tableau comme "entrée" de StructuredViewer au lieu d'une collection Java).
Exemple d'appel de la méthode:
la source
Dans le monde de la session HTTP, vous ne pouvez pas vraiment éviter la distribution, car l'API est écrite de cette façon (prend et retourne uniquement
Object
).Cependant, avec un peu de travail, vous pouvez facilement éviter le casting non contrôlé. Cela signifie qu'il se transformera en un casting traditionnel donnant un
ClassCastException
droit là en cas d'erreur). Une exception non vérifiée pourrait se transformer en unCCE
à tout moment plus tard au lieu du point de la distribution (c'est la raison pour laquelle il s'agit d'un avertissement distinct).Remplacez le HashMap par une classe dédiée:
Ensuite, transtypez dans cette classe au lieu de
Map<String,String>
et tout sera vérifié à l'endroit exact où vous écrivez votre code. Pas d'inattenduClassCastExceptions
plus tard.la source
Dans Android Studio, si vous souhaitez désactiver l'inspection, vous pouvez utiliser:
la source
Dans ce cas particulier, je ne stockerais pas directement Maps dans la session HttpSession, mais plutôt une instance de ma propre classe, qui à son tour contient une carte (un détail d'implémentation de la classe). Vous pouvez alors être sûr que les éléments de la carte sont du bon type.
Mais si vous voulez quand même vérifier que le contenu de la carte est de bon type, vous pouvez utiliser un code comme celui-ci:
la source
La fonction utilitaire Objects.Unchecked dans la réponse ci-dessus par Esko Luontola est un excellent moyen d'éviter l'encombrement du programme.
Si vous ne voulez pas les SuppressWarnings sur une méthode entière, Java vous oblige à le mettre sur un local. Si vous avez besoin d'un casting sur un membre, cela peut conduire à un code comme celui-ci:
L'utilisation de l'utilitaire est beaucoup plus propre et il est toujours évident de savoir ce que vous faites:
REMARQUE: je pense qu'il est important d'ajouter que parfois l'avertissement signifie vraiment que vous faites quelque chose de mal comme:
Ce que le compilateur vous dit, c'est que cette conversion NE sera PAS vérifiée au moment de l'exécution, donc aucune erreur d'exécution ne sera déclenchée jusqu'à ce que vous essayiez d'accéder aux données dans le conteneur générique.
la source
La suppression des avertissements n'est pas une solution. Vous ne devriez pas faire de casting à deux niveaux dans une seule déclaration.
la source
Une supposition rapide si vous publiez votre code peut dire avec certitude, mais vous pourriez avoir fait quelque chose dans le sens de
ce qui produira l'avertissement lorsque vous devrez faire
il pourrait être utile de regarder
Génériques dans le langage de programmation Java
si vous n'êtes pas familier avec ce qui doit être fait.
la source
J'ai peut-être mal compris la question (un exemple et quelques lignes environnantes seraient bien), mais pourquoi n'utilisez-vous pas toujours une interface appropriée (et Java5 +)? Je ne vois aucune raison pour laquelle vous voudriez jamais lancer un vers au
HashMap
lieu d'unMap<KeyType,ValueType>
. En fait, je ne peux imaginer aucune raison de définir le type d'une variable à laHashMap
place deMap
.Et pourquoi la source est-elle un
Object
? S'agit-il d'un type de paramètre d'une collection héritée? Si tel est le cas, utilisez des génériques et spécifiez le type souhaité.la source
Si je dois utiliser une API qui ne prend pas en charge les génériques .. J'essaie d'isoler ces appels dans des routines wrapper avec le moins de lignes possible. J'utilise ensuite l'annotation SuppressWarnings et j'ajoute également les modèles de sécurité de type en même temps.
C'est juste une préférence personnelle pour garder les choses aussi propres que possible.
la source
Prenez celui-ci, c'est beaucoup plus rapide que de créer un nouveau HashMap, s'il en est déjà un, mais toujours sécurisé, car chaque élément est comparé à son type ...
la source
key.isAssignableFrom(e.getKey().getClass())
peut être écrit commekey.isInstance(e.getKey())
Vérifiez-le simplement avant de le lancer.
Et pour quiconque le demande, il est assez courant de recevoir des objets dont vous n'êtes pas sûr du type. De nombreuses implémentations "SOA" héritées transmettent divers objets auxquels vous ne devez pas toujours faire confiance. (Les horreurs!)
EDIT Modification du code d'exemple une fois pour correspondre aux mises à jour de l'affiche, et après quelques commentaires, je vois que instanceof ne fonctionne pas bien avec les génériques. Cependant, changer la vérification pour valider l'objet externe semble bien fonctionner avec le compilateur en ligne de commande. Exemple révisé maintenant affiché.
la source
Presque tous les problèmes en informatique peuvent être résolus en ajoutant un niveau d'indirection *, ou quelque chose.
Introduisez donc un objet non générique d'un niveau supérieur à celui d'un
Map
. Sans contexte, cela ne semble pas très convaincant, mais de toute façon:* Sauf trop de niveaux d'indirection.
la source
Voici une façon de gérer cela lorsque je remplace l'
equals()
opération.Cela semble fonctionner en Java 8 (même compilé avec
-Xlint:unchecked
)la source
Si vous êtes sûr que le type renvoyé par session.getAttribute () est HashMap, vous ne pouvez pas transtyper ce type exact, mais vous fier à ne vérifier que le HashMap générique
Eclipse surprendra alors les avertissements, mais cela peut bien sûr entraîner des erreurs d'exécution qui peuvent être difficiles à déboguer. J'utilise cette approche uniquement dans des contextes non critiques.
la source
Deux façons, l'une qui évite complètement la balise, l'autre utilisant une méthode utilitaire coquine mais agréable.
Le problème est les collections pré-génériques ...
Je crois que la règle de base est la suivante: "cast les objets une chose à la fois" - ce que cela signifie lorsque vous essayez d'utiliser des classes brutes dans un monde générique, c'est parce que vous ne savez pas quoi est dans cette carte <?,?> (et en effet la JVM pourrait même trouver que ce n'est même pas une carte!), il est évident quand on y pense que vous ne pouvez pas le lancer. Si vous aviez un Map <String,?> Map2 alors HashSet <String> keys = (HashSet <String>) map2.keySet () ne vous donne pas d'avertissement, bien qu'il s'agisse d'un "acte de foi" pour le compilateur (car cela pourrait se révéler être un TreeSet) ... mais ce n'est qu'un seul acte de foi.
PS à l'objection selon laquelle itérer comme dans ma première façon "est ennuyeux" et "prend du temps", la réponse est "pas de douleur pas de gain": une collection générique est garantie pour contenir Map.Entry <String, String> s, et rien autre. Vous devez payer pour cette garantie. Lorsque vous utilisez systématiquement des génériques, ce paiement prend magnifiquement la forme de la conformité du codage, pas du temps machine!
Une école de pensée pourrait dire que vous devriez définir les paramètres d'Eclipse pour faire de telles erreurs de cast non contrôlées, plutôt que des avertissements. Dans ce cas, vous devrez utiliser ma première méthode.
la source
Cela fait disparaître les avertissements ...
la source
Solution: désactivez cet avertissement dans Eclipse. Ne @SuppressWarnings pas, désactivez-le complètement.
Plusieurs des "solutions" présentées ci-dessus sont très éloignées, rendant le code illisible pour supprimer un avertissement stupide.
la source
@SuppressWarnings
ne rend pas du tout le code illisible.