Quand utiliser une assertion et quand utiliser une exception

121

La plupart du temps, j'utiliserai une exception pour vérifier une condition dans mon code, je me demande quand est-il opportun d'utiliser une assertion?

Par exemple,

Group group=null;
try{
    group = service().getGroup("abc");
}catch(Exception e){
    //I dont log error because I know whenever error occur mean group not found
}

if(group !=null)
{
    //do something
}

Pouvez-vous indiquer comment une assertion s'inscrit ici? Dois-je utiliser une assertion?

Il semble que je n'utilise jamais d'assertions dans le code de production et que je ne vois que les assertions dans les tests unitaires. Je sais que dans la plupart des cas, je peux simplement utiliser l'exception pour faire la vérification comme ci-dessus, mais je veux savoir comment le faire "professionnellement".

cometta
la source

Réponses:

81

Les assertions devraient être utilisées pour vérifier quelque chose qui ne devrait jamais arriver, tandis qu'une exception devrait être utilisée pour vérifier quelque chose qui pourrait arriver.

Par exemple, une fonction peut diviser par 0, donc une exception doit être utilisée, mais une assertion peut être utilisée pour vérifier que le disque dur disparaît soudainement.

Une assertion arrêterait l'exécution du programme, mais une exception permettrait au programme de continuer à s'exécuter.

Notez que ce if(group != null)n'est pas une assertion, c'est juste un conditionnel.

Marius
la source
3
"une assertion pourrait être utilisée pour vérifier que le disque dur disparaît soudainement" - je dirais que ce n'est pas correct: pourquoi voudriez-vous que cela soit traité pendant le développement, mais pas en production (lorsque les assertions sont généralement désactivées)?
herman le
71
Le commentaire sur le disque dur est faux. Les assertions servent à vérifier les erreurs dans votre logique de code. Jamais, jamais, ne les utilisez pour vérifier quelque chose que vous ne contrôlez pas. N'oubliez pas que si une assertion échoue, cela signifie que votre code est erroné .
Ian Gold du
1
@Marius Votre conditionnel pourrait être remplacé par une assertion comme celle-ci: assert group! = Null
IgorGanapolsky
1
Évalué parce que l'exemple du disque dur contredit votre propre philosophie. La «disparition» des disques durs (du point de vue du code) pourrait en fait se produire dans la réalité - aussi improbable soit-elle. Comme le dit @IanGoldby, les assertions doivent dépendre uniquement des éléments contrôlés par votre code.
Vicky Chijwani
Une bien meilleure réponse si publiée par Gregory Pakosz, veuillez lire cet article.
ormurin
169

Hors de ma tête (la liste peut être incomplète et trop longue pour tenir dans un commentaire), je dirais:

  • utiliser des exceptions lors de la vérification des paramètres passés aux méthodes et constructeurs publics ou protégés
  • utiliser des exceptions lors de l'interaction avec l'utilisateur ou lorsque vous vous attendez à ce que le code client se rétablisse d'une situation exceptionnelle
  • utiliser des exceptions pour résoudre les problèmes qui pourraient survenir
  • utiliser des assertions lors de la vérification des pré-conditions, post-conditions et invariants du code privé / interne
  • utiliser des assertions pour fournir des commentaires à vous-même ou à votre équipe de développeurs
  • utiliser des assertions lors de la recherche de choses qui sont très improbables sinon cela signifie qu'il y a un défaut sérieux dans votre application
  • utiliser des assertions pour énoncer des choses que vous savez (supposément) être vraies

En d'autres termes, les exceptions concernent la robustesse de votre application tandis que les assertions traitent de son exactitude.

Les assertions sont conçues pour être bon marché à écrire, vous pouvez les utiliser presque partout et j'utilise cette règle de base: plus une déclaration d'assertion semble stupide, plus elle est précieuse et plus elle intègre d'informations. Lors du débogage d'un programme qui ne se comporte pas correctement, vous vérifierez sûrement les possibilités d'échec les plus évidentes en fonction de votre expérience. Ensuite, vous vérifierez les problèmes qui ne peuvent tout simplement pas arriver: c'est exactement à ce moment que les affirmations aident beaucoup et font gagner du temps.

Gregory Pakosz
la source
53
J'aime la façon dont vous avez formulé ceci: les exceptions concernent la robustesse de votre application tandis que les affirmations concernent son exactitude .
M. Dudley
J'ai posté ceci sur mon site: pempek.net/articles/2013/11/16/assertions-or-exceptions
Gregory Pakosz
Et si vous cherchez une bibliothèque d'assert personnalisée C ++, j'ai publié github.com/gpakosz/Assert
Gregory Pakosz
26

N'oubliez pas que les assertions peuvent être désactivées lors de l'exécution à l'aide de paramètres et sont désactivées par défaut , alors ne comptez pas sur elles sauf à des fins de débogage.

Vous devriez également lire l' article d'Oracle sur assert pour voir plus de cas où utiliser - ou ne pas utiliser - assert.

chburd
la source
Hue hue hue Je me demandais pourquoi mon code échouait dans Eclipse mais fonctionnait bien sur la ligne de commande.
Steve
15

En règle générale:

  • Utilisez des assertions pour les contrôles de cohérence internes là où il n'y a aucune importance si quelqu'un les désactive. (Notez que la javacommande désactive toutes les assertions par défaut.)
  • Utilisez des tests réguliers pour tout type de vérification de ce qui ne doit pas être désactivé. Cela inclut les contrôles défensifs qui se prémunissent contre les dommages potentiels causés par des bogues, et toutes les données / demandes de validation / tout ce qui est fourni par les utilisateurs ou les services externes.

Le code suivant de votre question est de mauvais style et potentiellement bogué

try {
    group = service().getGroup("abc");
} catch (Exception e) {
    //i dont log error because i know whenever error occur mean group not found
}

Le problème est que vous ne savez PAS qu'une exception signifie que le groupe n'a pas été trouvé. Il est également possible que l' service()appel ait levé une exception, ou qu'il ait renvoyé nullce qui a ensuite provoqué un NullPointerException.

Lorsque vous détectez une exception «attendue», vous ne devez intercepter que l'exception que vous attendez. En attrapant java.lang.Exception(et surtout en ne le journalisant pas), vous rendez plus difficile le diagnostic / débogage du problème, et potentiellement en permettant à l'application de faire plus de dégâts.

Stephen C
la source
4

Eh bien, chez Microsoft, la recommandation était de lancer des exceptions dans toutes les API que vous mettez à disposition du public et d'utiliser des Asserts dans toutes sortes d'hypothèses que vous faites sur le code interne. C'est un peu une définition vague, mais je suppose que c'est à chaque développeur de tracer la ligne.

En ce qui concerne l'utilisation des exceptions, comme son nom l'indique, leur utilisation doit être exceptionnelle, donc pour le code que vous présentez ci-dessus, l' getGroupappel doit revenir nullsi aucun service n'existe. Une exception ne devrait se produire que si une liaison réseau tombe en panne ou quelque chose du genre.

Je suppose que la conclusion est qu'il appartient un peu à l'équipe de développement de chaque application de définir les limites entre assert et exceptions.

rui
la source
À mon humble avis, le problème avec ce type de recommandation est que tout va bien tant que la frontière entre les parties publique et privée d'une API est assez fixe. Si vous développez un nouveau code, cette limite est souvent assez fluide ...
Len Holgate
Oui tu as raison. C'est une ligne directrice, mais en fin de compte, c'est laissé à la sensibilité des programmeurs. Je ne pense pas qu'il y ait une ligne de définition ultime pour ceux-ci, donc je suppose que vous allez simplement avec ce que vous pensez que c'est juste en lisant des tas de codes différents.
rui
3

Selon ce document http://docs.oracle.com/javase/6/docs/technotes/guides/language/assert.html#design-faq-general , "L'instruction assert est appropriée pour les précondition, postcondition et invariant de classe non publiques La vérification des conditions préalables publiques doit toujours être effectuée par des vérifications à l'intérieur des méthodes qui entraînent des exceptions documentées particulières, telles que IllegalArgumentException et IllegalStateException. "

Si vous voulez en savoir plus sur la précondition, la postcondition et l'invariant de classe, consultez ce document: http://docs.oracle.com/javase/6/docs/technotes/guides/language/assert.html#usage-conditions . Il contient également des exemples d'utilisation des assertions.

cesarsalgado
la source
1

Le test de null n'attrapera que les nulls causant des problèmes, alors qu'un try / catch tel que vous l'avez détectera toute erreur.

En gros, try / catch est plus sûr, mais légèrement plus lent, et vous devez faire attention à ce que vous détectiez tous les types d'erreurs qui peuvent survenir. Donc, je dirais d'utiliser try / catch - un jour, le code getGroup peut changer, et vous pourriez avoir besoin de ce plus grand réseau.

Phil H
la source
1

Vous pouvez utiliser cette simple différence à l'esprit lors de leur utilisation. Les exceptions seront utilisées pour vérifier les erreurs attendues et inattendues appelées erreur vérifiée et non vérifiée tandis que l'assertion est principalement utilisée à des fins de débogage au moment de l'exécution pour voir si les hypothèses sont validées ou non.

SBTec
la source
1

J'avoue que je suis un peu confus par votre question. Lorsqu'une condition d'assertion n'est pas remplie, une exception est levée. Confusément, cela s'appelle AssertionError . Notez qu'elle n'est pas cochée, comme (par exemple) IllegalArgumentException qui est lancée dans des circonstances très similaires.

Donc, en utilisant des assertions en Java

  1. est un moyen plus concis d'écrire un bloc condition / throw
  2. vous permet d'activer / de désactiver ces vérifications via les paramètres JVM. Normalement, je laisserais ces vérifications tout le temps, à moins qu'elles n'aient un impact sur les performances d'exécution ou aient une pénalité similaire.
Brian Agnew
la source
AssertionError est une sous-classe de Error et non de RuntimeException.
Stephen C
Ah. Bien sûr. Je pensais coché / décoché. Maintenant corrigé
Brian Agnew
Il explique ce que sont les affirmations (d'un point de vue controversé), mais pas quand les utiliser exactement.
Karl Richter
1

Voir la section 6.1.2 (Assertions vs autre code d'erreur) de la documentation de Sun au lien suivant.

http://www.oracle.com/technetwork/articles/javase/javapch06.pdf

Ce document donne les meilleurs conseils que j'ai vus sur le moment d'utiliser les assertions. Citant le document:

"En règle générale, vous devez utiliser une assertion pour les cas exceptionnels que vous aimeriez oublier. Une assertion est le moyen le plus rapide de traiter et d'oublier une condition ou un état que vous ne pensez pas devoir traiter avec."

Phil
la source
0

Malheureusement, les assertions peuvent être désactivées. Lorsque vous êtes en production, vous avez besoin de toute l'aide que vous pouvez obtenir pour repérer quelque chose d'imprévu, alors les affirmations se disqualifient.

Thorbjørn Ravn Andersen
la source