web-dev-qa-db-fra.com

Qu'est-ce qu'un casting non vérifié et comment puis-je le vérifier?

Je pense que je comprends ce que signifie une distribution non vérifiée (une diffusion d'un type à un autre), mais que signifie "Vérifier" la distribution? Comment puis-je vérifier le casting afin d'éviter cet avertissement dans Eclipse?

29
SIr Codealot

Une diffusion non contrôlée signifie que vous convertissez (implicitement ou explicitement) d'un type générique vers un type non qualifié ou l'inverse. Par exemple. cette ligne

Set<String> set = new HashSet();

produira un tel avertissement.

Il y a généralement une bonne raison pour de tels avertissements, vous devriez donc essayer d'améliorer votre code au lieu de supprimer l'avertissement. Extrait de Effective Java, 2e édition:

Éliminer tous les avertissements non contrôlés que vous pouvez. Si Élimine tous les avertissements, vous êtes assuré que votre code est dactylographié, ce qui est une très Bonne chose. Cela signifie que vous n’obtiendrez pas de ClassCastException au moment de l’exécution et que Augmente votre confiance dans le fait que votre programme se comporte comme prévu.

Si vous ne pouvez pas éliminer un avertissement et que vous pouvez prouver que le code que A provoqué l’avertissement est dactylographié, alors (et seulement alors) supprimez l’avertissement Avec une annotation @SuppressWarnings("unchecked"). Si vous supprimez les avertissements Sans d'abord prouver que le code est dactylographié, vous ne vous donnez qu'un faux Sentiment de sécurité. Le code peut être compilé sans émettre d’avertissements, mais Peut toujours générer une variable ClassCastException au moment de l’exécution. Si, toutefois, vous ignorez Les avertissements non contrôlés que vous savez être sûrs (au lieu de les supprimer), vous Ne remarquerez pas quand un nouvel avertissement apparaît qui représente un réel problème. Le nouvel avertissement Sera perdu parmi toutes les fausses alarmes que vous n’avez pas mises au silence.

Bien entendu, il n'est pas toujours aussi facile d'éliminer les avertissements qu'avec le code ci-dessus. Sans voir votre code, il n'y a aucun moyen de savoir comment le rendre sûr.

32
Péter Török

Pour approfondir ce que Peter a écrit:

Les transtypages de types non génériques vers les types génériques peuvent très bien fonctionner au moment de l'exécution, car les paramètres génériques sont effacés lors de la compilation, nous avons donc une distribution légitime. Toutefois, le code peut échouer ultérieurement avec une exception ClassCastException inattendue en raison d'une hypothèse erronée concernant le paramètre type. Par exemple:

List l1 = new ArrayList();
l1.add(33);
ArrayList<String> l2 = (ArrayList<String>) l1;
String s = l2.get(0);

L'avertissement non vérifié de la ligne 3 indique que le compilateur n'est plus en mesure de garantir la sécurité du type, en ce sens qu'une exception ClassCastException inattendue peut se produire ultérieurement. Et cela se produit à la ligne 4, qui effectue une conversion implicite.

43
Eyal Schneider

Une distribution non contrôlée, par opposition à la distribution contrôlée, ne vérifie pas la sécurité du type au moment de l'exécution. 

Voici un exemple basé sur la section Consider typesafe heterogenous containers de la 3ème éd. de "Effective Java" de Joshua Bloch, mais la classe de conteneur est intentionnellement cassée - elle stocke et renvoie le type incorrect:

public class Test {

    private static class BrokenGenericContainer{
        private final Map<Class<?>, Object> map= new HashMap<>();

        public <T> void store(Class<T> key, T value){
            map.put(key, "broken!"); // should've been [value] here instead of "broken!"
        }

        public <T> T retrieve(Class<T> key){
//          return key.cast(map.get(key)); // a checked cast 
            return (T)map.get(key);        // an unchecked cast
        }

    }

    public static void main(String[] args) {
        BrokenGenericContainer c= new BrokenGenericContainer();
        c.store(Integer.class, 42);
        List<Integer> ints = new ArrayList<>();
        ints.add(c.retrieve(Integer.class));
        Integer i = ints.get(0);
    }

}


Si la retrieve() utilise une transtypage non contrôlé _ -(T)map.get(key) - l'exécution de ce programme entraînera la génération de ClassCastException à la Integer i = ints.get(0). La méthode retrieve() se terminera car le type réel n'a pas été vérifié à l'exécution:

Exception in thread "main" 
Java.lang.ClassCastException: Java.lang.String cannot be cast to Java.lang.Integer
    at Test.main(Test.Java:27)


Mais si la retrieve() utilise une distribution vérifiée - key.cast(map.get(key)) - l'exécution de ce programme entraînera l'apparition de ClassCastException à la key.cast(map.get(key)), car la distribution vérifiée déterminera que le type est faux et jeter l'exception. La méthode retrieve() ne sera pas terminée:

Exception in thread "main" Java.lang.ClassCastException: 
                                          Cannot cast Java.lang.String to Java.lang.Integer
    at Java.lang.Class.cast(Class.Java:3369)
    at Test$BrokenGenericContainer.retrieve(Test.Java:16)
    at Test.main(Test.Java:26)

Cela peut sembler peu différent, mais dans le cas de la distribution non contrôlée, un String est entré avec succès dans un List<Integer>. Dans le monde réel, les conséquences peuvent être graves. En cas de vérification de la distribution, la disparité de type a été découverte le plus tôt possible.


@SuppressWarnings("unchecked") peut être utilisé si le programmeur est vraiment sûr que la méthode est réellement sûre. La meilleure solution consiste à utiliser des médicaments génériques et des versions vérifiées lorsque cela est possible.

Comme Joshua Bloch l'a dit,

... les avertissements non contrôlés sont importants. Ne les ignorez pas.


Par souci d’exhaustivité, this answer traite des spécificités d’Eclipse.

0
jihor