web-dev-qa-db-fra.com

Lancer activement AssertionError dans les bonnes pratiques Java?

En parcourant 'Effective Java - Second Edition' de Joshua Bloch, je suis tombé sur le code suivant à la page 152:

double apply(double x, double y) {
    switch(this) {
        case PLUS:   return x + y;
        case MINUS:  return x - y;
        case TIMES:  return x * y;
        case DIVIDE: return x / y;
    }
    throw new AssertionError("Unknown op: " + this);
}

Maintenant, ce qui me trouble, c'est que la AssertionError est lancée activement. Est-ce que cela est considéré comme une bonne pratique? À ma connaissance, les assertions sont utilisées pour ne pas interférer avec le code, de sorte que lorsque la programmation Java est lancée sans que les assertions ne soient activées et que les instructions assert ne soient donc pas exécutées, le comportement ne change pas Je serais plutôt confus si j'obtenais une AssertionException lorsque je lance un programme sans même que les assertions soient activées.

Même si je comprends que le cas type peut arriver assez souvent, que vous analysez deux options différentes et que si ce n’est pas le cas, vous devriez lever une exception.

Alors, est-ce une bonne pratique de lancer une AssertionException ici, ou serait-il préférable d’en lancer une autre? Si oui, lequel conviendrait le mieux? Peut-être que IllegalArgumentException?


Éditer pour clarifier: Ma question ne concerne pas la question de savoir si nous devrions jeter une Error ici, mais si nous voulons lancer une Exception ou une Error, laquelle choisir? Et est-ce une bonne pratique de lancer activement AssertionErrors? La documentation dit Jeté pour indiquer qu’une assertion a échoué , j’ai le sentiment que nous ne devrions pas la lancer activement. Est-ce exact?


Deuxième édition: question claire: est-ce une bonne pratique de lancer activement une AssertionError ou faut-il éviter cela, même si c'est possible? (Je suppose que la lecture de la documentation est la dernière)

22
Mathias Bader

À mon avis, une AssertionError serait incorrecte à utiliser ici.

À partir de la documentation , un AssertionError étend la classe de base Error

Une erreur est une sous-classe de Throwable qui indique des problèmes graves qu’une application raisonnable ne devrait pas tenter de résoudre.

Une erreur devrait être fatale, alors que j'attendrais de votre programme qu'il gère cela et affiche un message d'avertissement à l'utilisateur concernant l'opération inconnue.

Si quelque chose ici, je m'attendrais à ce qu'une UnsupportedOperationException soit lancée et traitée ailleurs dans la pile d'appels.

Lancé pour indiquer que l'opération demandée n'est pas prise en charge.

Prenons le cas où, non pas dans une calculatrice, mais dans tout flux de code utilisant des ENUM:

Si un développeur devait ajouter une nouvelle valeur à une énumération existante, je ne m'attendrais pas à ce que les fonctions qui utilisent cette énumération existante invoquent une erreur, simplement parce que la nouvelle valeur n'est pas prise en charge.

5
Matt Clark

Concernant les erreurs, le Tutoriel Java indique:

Le deuxième type d’exception est l’erreur. Ce sont des conditions exceptionnelles qui sont externes à l'application et que l'application ne peut généralement pas anticiper ni récupérer.

De plus, le guide Programmation avec assertions indique:

N'utilisez pas d'assertions pour la vérification d'arguments dans les méthodes publiques.

Je pense donc que Exception est le bon moyen de vérifier ce type de cas.

Je recommande d'utiliser new UnsupportedOperationException("Operator " + name() + " is not supported."); car il décrit mieux le problème à mon avis (c'est-à-dire qu'un développeur a ajouté une valeur enum mais a oublié d'implémenter le cas requis).

Cependant, je pense que cet exemple de cas devrait utiliser un modèle de conception AbstractEnum au lieu d'un commutateur:

PLUS {
    double apply(double x, double y) {
        return x + y;
    }
},
MINUS {
    double apply(double x, double y) {
        return x - y;
    }
},
TIMES {
    double apply(double x, double y) {
        return x * y;
    }
},
DIVIDE {
    double apply(double x, double y) {
        return x / y;
    }
};

abstract double apply(double x, double y);

Il est moins sujet aux erreurs puisque ce code ne sera pas compilé avant que chaque cas implémente apply.

4
Raphaël

Je préférerais

    double apply(double x, double y) {
    switch(this) {
        case PLUS:   return x + y;
        case MINUS:  return x - y;
        case TIMES:  return x * y;
        default: assert this==DIVIDE: return x / y;
    }
}
  1. Nous ne devrions pas lancer AssertionError car cela devrait être réservé aux assertions réelles.
  2. Hormis les assertions et certains blocs catch, il ne devrait pas y avoir un seul bit de code impossible à atteindre.

Mais je préférerais davantage https://stackoverflow.com/a/41324246/348975

2
emory

Je pense que les deux AssertionError ou IllegalAE ne sont pas très bons ici. L'affirmation d'erreur n'est pas bonne, comme l'indique la réponse de Matt. Et les arguments ne sont pas faux ici, ils sont simplement passés à une méthode sur la mauvaise opération this. Donc, IAE peut ne pas être bon aussi. Bien sûr, il s’agit là d’une question et d’une réponse basées sur les opinions.

De plus, je ne suis pas sûr que l'activation d'une assertion soit obligatoire pour lancer AssertionError ou une assertionError signifie que les assertions ont été activées.

1
Atul

Si je comprends bien, votre méthode est une méthode d'objet enum. Dans la plupart des cas, lorsque quelqu'un ajoute une nouvelle valeur enum, il doit également modifier la méthode "apply". Dans ce cas, vous devriez lever une exception UnsupportedOperationException.

0
Ivan Dubynets