Que fait le mot clé Java assert et quand doit-il être utilisé?
Ce sont quelques-uns exemples concrets pour comprendre le rôle clé des assertions?
17 réponses
Assertions (via le mot-clé assert ) ont été ajoutés en Java 1.4. Ils sont utilisés pour vérifier l'exactitude d'un invariant dans le code. Elles ne doivent jamais être déclenchées dans le code de production, et sont indicatives d'un bug ou d'un mauvais usage d'un chemin de code. Ils peuvent être activés à l'exécution par l'option -ea
sur la commande java
, mais ne sont pas activés par défaut.
un exemple:
public Foo acquireFoo(int id) {
Foo result = null;
if (id > 50) {
result = fooService.read(id);
} else {
result = new Foo(id);
}
assert result != null;
return result;
}
supposons que vous êtes censé écrire un programme pour contrôler une centrale nucléaire. Il est assez évident que même la plus petite erreur pourrait avoir des résultats catastrophiques, par conséquent votre code doit être sans bogue (en supposant que le JVM est sans bogue pour le bien de l'argument).
Java n'est pas un langage vérifiable, ce qui signifie: Vous ne pouvez pas calculer que le résultat de votre opération sera parfait. La principale raison de ce sont pointeurs: ils peuvent pointer n'importe où ou nulle part, donc ils ne peuvent pas être calculés pour être de cette valeur exacte, au moins pas dans un intervalle raisonnable de code. Compte tenu de ce problème, il n'y a aucun moyen de prouver que votre code est correct dans l'ensemble. Mais ce que vous pouvez faire est de prouver que vous au moins trouver chaque bug quand il se produit.
cette idée est basée sur le paradigme Design-by-Contract (DbC): vous définissez d'abord (avec précision mathématique) ce que votre méthode est censé faire, puis vérifier cela en le testant au cours de l'exécution réelle. Exemple:
// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
return a + b;
}
bien que cela soit assez évident pour fonctionner correctement, la plupart des programmeurs ne verront pas le bug caché à l'intérieur de celui-ci (indice: L'Ariane V s'est écrasé à cause d'un bug similaire). Maintenant DbC définit que vous devez toujours vérifier l'entrée et la sortie d'une fonction pour vérifier qu'il fonctionne correctement. Java peut le faire par des affirmations:
// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
assert (Integer.MAX_VALUE - a >= b) : "Value of " + a + " + " + b + " is too large to add.";
final int result = a + b;
assert (result - a == b) : "Sum of " + a + " + " + b + " returned wrong sum " + result;
return result;
}
si cette fonction échoue, vous le remarquerez. Vous saurez qu'il y a un problème dans votre code, vous savez où il est et vous savez ce qui l'a causé (similaire aux Exceptions). Et ce qui est encore plus important: vous arrêtez d'exécuter correctement lorsque cela arrive pour empêcher tout autre code de fonctionner avec de mauvaises valeurs et de causer potentiellement des dommages à tout ce qu'il contrôle.
Java Exceptions sont un concept similaire, mais ils ne parviennent pas à tout vérifier. Si vous voulez encore plus de contrôles (au détriment de la vitesse d'exécution), vous devez utiliser les assertions. Faire cela gonflera votre code, mais vous pouvez en fin de compte livrer un produit à un temps de développement étonnamment court (plus tôt vous corrigez un bug, plus faible est le coût). Et en plus: si il y a un bug dans votre code, vous le détecter. Il n'y a aucun moyen qu'un bug glisse et cause des problèmes plus tard.
ce n'est toujours pas une garantie pour le code sans bogue, mais c'est beaucoup plus proche de cela, que les programmes habituels.
Assertions sont un outil en phase de développement pour détecter les bogues dans votre code. Elles sont conçues pour être facilement enlevées, donc elles n'existeront pas dans le code de production. Ainsi, les assertions ne font pas partie de la "solution" que vous livrez au client. Ce sont des vérifications internes pour s'assurer que les hypothèses que vous faites sont correctes. L'exemple le plus courant est le test null. Beaucoup de méthodes sont écrites comme ceci:
void doSomething(Widget widget) {
if (widget != null) {
widget.someMethod(); // ...
... // do more stuff with this widget
}
}
très souvent dans une méthode comme celle-ci, le widget ne devrait jamais être nulle. Donc si c'est nul, il y a un bug dans votre code quelque part que vous devez traquer. Mais le code ci-dessus ne vous dira jamais ce. Donc dans un effort bien intentionné pour écrire un code "sûr", vous cachez aussi un bug. C'est beaucoup mieux d'écrire du code comme ceci:
/**
* @param Widget widget Should never be null
*/
void doSomething(Widget widget) {
assert widget != null;
widget.someMethod(); // ...
... // do more stuff with this widget
}
de Cette façon, vous serez sûr d'attraper ce bug au début. (Il est également utile de préciser dans le contrat que ce paramètre ne doit jamais être nul.) Assurez-vous de mettre les assertions sur quand vous testez votre code pendant le développement. (Et il est souvent difficile de convaincre vos collègues de le faire, ce que je trouve très ennuyeux.)
maintenant, certains de vos collègues vont objecter à ce code, en faisant valoir que vous devriez toujours mettre le chèque nul pour empêcher une exception dans la production. Dans ce cas, l'affirmation est toujours utile. Vous pouvez l'écrire comme ceci:
void doSomething(Widget widget) {
assert widget != null;
if (widget != null) {
widget.someMethod(); // ...
... // do more stuff with this widget
}
}
de cette façon, vos collègues seront heureux que le chèque nul est là pour le code de production, mais pendant le développement, vous ne cachez plus le bogue lorsque le widget est null.
voici un exemple du monde réel: j'ai écrit une fois une méthode qui comparait deux valeurs arbitraires pour l'égalité, où l'une ou l'autre valeur pourrait être nulle:
/**
* Compare two values using equals(), after checking for null.
* @param thisValue (may be null)
* @param otherValue (may be null)
* @return True if they are both null or if equals() returns true
*/
public static boolean compare(final Object thisValue, final Object otherValue) {
boolean result;
if (thisValue == null) {
result = otherValue == null;
} else {
result = thisValue.equals(otherValue);
}
return result;
}
ce code délègue le travail de la méthode equals()
dans le cas où cette valeur n'est pas nulle. Mais il suppose que la méthode equals()
remplit correctement le contrat de equals()
par gérer correctement un paramètre null.
un collègue s'est opposé à mon code, me disant que beaucoup de nos classes ont buggy equals()
méthodes qui ne test pas pour null, donc je devrais mettre cette vérification dans cette méthode. C'est discutable si c'est sage, ou si nous devrions forcer l'erreur, pour que nous puissions la repérer et la corriger, mais je me suis reporté à mon collègue et mis dans une vérification nulle, que j'ai marqué avec un commentaire:
public static boolean compare(final Object thisValue, final Object otherValue) {
boolean result;
if (thisValue == null) {
result = otherValue == null;
} else {
result = otherValue != null && thisValue.equals(otherValue); // questionable null check
}
return result;
}
le contrôle supplémentaire ici, other != null
, n'est nécessaire que si la méthode equals()
ne permet pas de vérifier la nullité comme l'exige son contrat.
plutôt que d'engager un débat stérile avec mon collègue sur la sagesse de laisser le code buggy rester dans notre base de code, je mets simplement deux affirmations dans le code. Ces affirmations me feront savoir, pendant la phase de développement, si l'une de nos classes échoue à mettre en œuvre correctement equals()
, de sorte que je puisse la corriger:
public static boolean compare(final Object thisValue, final Object otherValue) {
boolean result;
if (thisValue == null) {
result = otherValue == null;
assert otherValue == null || otherValue.equals(null) == false;
} else {
result = otherValue != null && thisValue.equals(otherValue);
assert thisValue.equals(null) == false;
}
return result;
}
les points importants à garder à l'esprit sont les suivants:
-
les Assertions ne sont que des outils en phase de développement.
-
le but d'une affirmation est de vous faire savoir s'il y a un bug, pas seulement dans votre code, mais dans votre base de code . (Les affirmations ici vont en fait signaler des bogues dans d'autres classes.)
-
même si mon collègue j'étais sûr que nos cours étaient bien écrits, les affirmations ici seraient toujours utiles. De nouvelles classes seront ajoutées qui pourraient ne pas réussir à tester null, et cette méthode peut signaler ces bogues pour nous.
-
dans le développement, vous devriez toujours tourner assertions sur, même si le code que vous avez écrit n'utilise pas assertions. Mon IDE est défini pour le faire toujours par défaut pour tout nouvel exécutable.
-
le les assertions ne changent pas le comportement du code dans la production, donc mon collègue est heureux que la vérification nulle soit là, et que cette méthode s'exécute correctement même si la méthode
equals()
est boguée. Je suis heureux parce que je vais attraper n'importe quel buggyequals()
méthode dans le développement.
de plus, vous devriez tester votre politique d'assertion en mettant dans une assertion temporaire qui échouera, de sorte que vous pouvez être certain que vous êtes notifié, soit par le biais de la fichier journal ou une trace de la pile dans le flux de sortie.
beaucoup de bonnes réponses expliquant ce que fait le mot-clé assert
, mais peu répondant à la vraie question, "quand le mot-clé assert
devrait-il être utilisé dans la vie réelle?"
La réponse: presque jamais .
"1519160920 des Assertions, en tant que concept, sont merveilleux. Bon code a beaucoup deif (...) throw ...
déclarations (et leurs parents comme Objects.requireNonNull
et Math.addExact
). Toutefois, certaines décisions de conception ont largement limité l'utilité de la assert
mot-clé lui-même.
l'idée maîtresse derrière le mot-clé assert
est l'optimisation prématurée, et la caractéristique principale est d'être en mesure de désactiver facilement tous les contrôles. En fait, les vérifications assert
sont désactivées par défaut.
cependant, il est d'une importance critique que les contrôles invariants continuent d'être faits dans la production. C'est parce que la couverture parfaite de test est impossible, et tout code de production aura des bogues qui assertions devraient aider à diagnostiquer et à atténuer.
par conséquent, l'utilisation de if (...) throw ...
devrait être préférée, tout comme elle est requise pour vérifier les valeurs des paramètres des méthodes publiques et pour lancer IllegalArgumentException
.
de temps en temps, on pourrait être tenté d'écrire un contrôle invariant qui prend un temps immanquablement long à traiter (et est appelé assez souvent pour qu'il importe). Toutefois, ces contrôles seront lents en bas des tests, ce qui est également indésirable. Ces vérifications fastidieuses sont habituellement rédigées sous forme de tests unitaires. Néanmoins, il peut parfois être judicieux d'utiliser assert
pour cette raison.
ne pas utiliser assert
simplement parce qu'il est plus propre et plus jolie que if (...) throw ...
(et je le dis avec beaucoup de douleur, parce que j'aime propre et jolie). Si vous ne pouvez pas vous aider vous-même, et peut contrôler la façon dont votre application est lancée, alors n'hésitez pas à utiliser assert
mais permettent toujours des assertions en production. certes, c'est ce que j'ai tendance à faire. Je pousse pour une annotation de lombok qui causera assert
d'agir plus comme if (...) throw ...
. votez pour ici.
(coup de gueule: la JVM devs étaient une bande d'affreux, prématurément l'optimisation des codeurs. C'est pourquoi vous entendez parler de tant de problèmes de sécurité dans le plugin Java et JVM. Ils ont refusé d'inclure des vérifications et des affirmations de base dans la production. code, et nous continuons à payer le prix.)
voici le cas d'usage le plus courant. Supposons que vous activez une valeur enum:
switch (fruit) {
case apple:
// do something
break;
case pear:
// do something
break;
case banana:
// do something
break;
}
tant que tu t'occupes de chaque affaire, tu vas bien. Mais un jour, quelqu'un ajoutera de la figue à votre enum et oubliera de l'ajouter à votre déclaration d'interrupteur. Cela produit un bug qui peut devenir difficile à attraper, parce que les effets ne seront pas ressentis avant que vous ayez quitté la déclaration de l'interrupteur. Mais si vous écrivez votre interrupteur comme ceci, vous pouvez l'attraper immédiatement:
switch (fruit) {
case apple:
// do something
break;
case pear:
// do something
break;
case banana:
// do something
break;
default:
assert false : "Missing enum value: " + fruit;
}
sont utilisées pour vérifier les conditions postérieures et les conditions préalables" ne devrait jamais échouer". Le code Correct ne doit jamais manquer une assertion; quand ils se déclenchent, ils doivent indiquer un bug (avec un peu de chance à un endroit qui est proche de l'endroit où se situe le problème).
Un exemple d'une affirmation peut être de vérifier qu'un groupe particulier de méthodes est appelée dans le bon ordre (par exemple, que hasNext()
est appelé avant next()
dans un Iterator
).
Que fait le mot-clé assert en Java?
regardons le bytecode compilé.
nous allons conclure que:
public class Assert {
public static void main(String[] args) {
assert System.currentTimeMillis() == 0L;
}
}
génère presque le même bytecode que:
public class Assert {
static final boolean $assertionsDisabled =
!Assert.class.desiredAssertionStatus();
public static void main(String[] args) {
if (!$assertionsDisabled) {
if (System.currentTimeMillis() != 0L) {
throw new AssertionError();
}
}
}
}
où Assert.class.desiredAssertionStatus()
est true
quand -ea
est passé sur la ligne de commande, et faux autrement.
nous utilisons System.currentTimeMillis()
pour nous assurer qu'il ne sera pas optimisé loin ( assert true;
did).
le champ synthétique est généré de sorte que Java n'a besoin d'appeler Assert.class.desiredAssertionStatus()
qu'une seule fois au moment de la charge, et il y cache le résultat. Voir aussi: Quelle est la signification de "synthétique statique"?
On peut vérifier que:
javac Assert.java
javap -c -constants -private -verbose Assert.class
avec Oracle JDK 1.8.0_45, un champ statique synthétique a été généré (voir aussi: Quelle est la signification de "synthétique statique"? ):
static final boolean $assertionsDisabled;
descriptor: Z
flags: ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
avec un initialiseur statique:
0: ldc #6 // class Assert
2: invokevirtual #7 // Method java/lang Class.desiredAssertionStatus:()Z
5: ifne 12
8: iconst_1
9: goto 13
12: iconst_0
13: putstatic #2 // Field $assertionsDisabled:Z
16: return
et la méthode principale est:
0: getstatic #2 // Field $assertionsDisabled:Z
3: ifne 22
6: invokestatic #3 // Method java/lang/System.currentTimeMillis:()J
9: lconst_0
10: lcmp
11: ifeq 22
14: new #4 // class java/lang/AssertionError
17: dup
18: invokespecial #5 // Method java/lang/AssertionError."<init>":()V
21: athrow
22: return
nous concluons que:
- il n'y a pas de support de niveau bytecode pour
assert
: c'est un concept de langage Java -
assert
pourrait être émulé plutôt bien avec les propriétés du système-Pcom.me.assert=true
pour remplacer-ea
sur la ligne de commande, et unthrow new AssertionError()
.
exemple du monde réel, tiré d'une classe de pile (tiré de Assertion dans les Articles en Java )
public int pop() {
// precondition
assert !isEmpty() : "Stack is empty";
return stack[--num];
}
en plus de toutes les bonnes réponses fournies ici, le guide de programmation Java SE 7 officiel a un manuel assez concis sur l'utilisation de assert
; avec plusieurs exemples ponctuels de quand il est une bonne (et, surtout, mauvaise) idée d'utiliser des assertions, et comment il est différent de lancer des exceptions.
une affirmation permet de détecter des défauts dans le code. Vous pouvez activer les assertions pour le test et le débogage tout en les laissant désactivées lorsque votre programme est en production.
pourquoi affirmer quelque chose quand on sait que c'est vrai? Ce n'est vrai que lorsque tout fonctionne correctement. Si le programme a un défaut, il pourrait ne pas être vrai. Le fait de le détecter plus tôt dans le processus permet de savoir que quelque chose ne va pas.
Un assert
déclaration contient cette déclaration ainsi qu'un message optionnel String
.
la syntaxe d'une affirmation a deux formes:
assert boolean_expression;
assert boolean_expression: error_message;
voici quelques règles de base qui régissent où les assertions doivent être utilisées et où elles ne doivent pas être utilisées. Les Assertions devrait :
-
validation des paramètres d'entrée d'une méthode privée. Non pour les méthodes publiques. Les méthodes
public
devraient lancer des exceptions régulières lorsqu'elles passent de mauvais paramètres. -
N'importe où dans le programme pour assurer la validité d'un fait qui est presque certainement vrai.
Par exemple, si vous êtes sûr qu'il sera seulement 1 ou 2, vous pouvez utiliser une affirmation comme:
...
if (i == 1) {
...
}
else if (i == 2) {
...
} else {
assert false : "cannot happen. i is " + i;
}
...
- Validation des conditions postérieures à la fin de n'importe quelle méthode. Cela signifie qu'après avoir exécuté la logique commerciale, vous pouvez utiliser des assertions pour vous assurer que l'état interne de vos variables ou de vos résultats est compatible avec ce que vous attendez. Par exemple, une méthode qui ouvre une socket ou un fichier peut utiliser une affirmation à la fin pour s'assurer que le support ou le fichier est ouvert.
Affirmations ne devrait pas :
-
Valider les paramètres d'entrée d'une méthode publique. Étant donné que les assertions ne sont pas toujours exécutées, le mécanisme d'exception habituel devrait être utilisé.
-
validation des contraintes sur quelque chose qui est entré par l'utilisateur. Même que ci-dessus.
-
ne doit pas être utilisé pour les effets indésirables.
par exemple, ce n'est pas une utilisation correcte car ici l'affirmation est utilisée pour son côté effet de l'appel de la doSomething()
la méthode.
public boolean doSomething() {
...
}
public void someMethod() {
assert doSomething();
}
le seul cas où cela pourrait être justifié est lorsque vous essayez de savoir si les assertions sont activées ou non dans votre code:
boolean enabled = false;
assert enabled = true;
if (enabled) {
System.out.println("Assertions are enabled");
} else {
System.out.println("Assertions are disabled");
}
Affirmer, c'est très utile lors du développement. Vous l'utilisez quand quelque chose juste ne peut pas arriver si votre code fonctionne correctement. Il est facile à utiliser, et peuvent rester dans le code pour toujours, car il sera désactivée dans la vie réelle.
S'il ya une chance que la condition peut se produire dans la vie réelle, alors vous devez gérer.
je l'aime, mais ne savez pas comment l'activer dans Eclipse/Android/ADT . Il semble être éteint même lors du débogage. (Il y a un thread sur ceci, mais il se réfère à La 'Java vm', qui n'apparaît pas dans la Configuration D'exécution ADT).
voici une affirmation que j'ai écrite dans un serveur pour un projet Hibernate/SQL. Un haricot d'entité a deux propriétés effectivement-booléennes, appelé isActive et isDefault. Chacune pouvait avoir une valeur de "Y" ou "N"ou null, qui était traitée comme "N". Nous voulons nous assurer que le client du navigateur est limité à ces trois valeurs. Ainsi, dans Mes setters pour ces deux propriétés, j'ai ajouté cette affirmation:
assert new HashSet<String>(Arrays.asList("Y", "N", null)).contains(value) : value;
notez ce qui suit.
-
cette affirmation ne concerne que la phase de développement. Si le client envoie une mauvaise valeur, nous attraperons cela tôt et le réparerons, bien avant que nous n'atteignions la production. Assertions sont pour les défauts que vous pouvez attraper tôt.
-
cette affirmation est lente et inefficace. Ce n'est pas grave. Les Assertions sont libres d'être lent. On s'en fiche car ce sont des outils de développement. Cela ne ralentira pas le code de production car les assertions seront désactivées. (Il y a un désaccord sur ce point, que j'aborderai plus tard.) Cela m'amène à mon prochain point.
-
cette affirmation n'a aucun effet secondaire. J'aurais pu tester ma valeur par rapport à un jeu final statique Invendable, mais ce jeu serait resté dans la production, où il ne serait jamais utilisé.
-
cette affirmation existe pour vérifier le bon fonctionnement du client. Donc, au moment où nous atteindrons la production, nous assurez-vous que le client fonctionne correctement, afin que nous puissions tourner l'affirmation off.
-
certaines personnes demandent ceci: si l'affirmation n'est pas nécessaire dans la production, Pourquoi ne pas simplement les enlever quand vous avez terminé? Parce que vous en aurez encore besoin quand vous commencerez à travailler sur la prochaine version.
certaines personnes ont fait valoir que vous ne devriez jamais utiliser des assertions, parce que vous ne pouvez jamais être sûr que tous les bogues sont partis, donc vous devez les garder autour même dans la production. Et donc il n'y a pas de raison d'utiliser la déclaration assert, puisque le seul avantage à asserts est que vous pouvez les désactiver. Par conséquent, selon cette pensée, vous ne devriez (presque) jamais utiliser les assertions. Je suis en désaccord. Il est certainement vrai que si un test appartient à la production, vous ne devriez pas utiliser une assertion. Mais ce test ne pas appartiennent à la production. Celui-ci est pour attraper un bug qui n'est pas susceptible d'atteindre la production, afin qu'il puisse être éteint en toute sécurité lorsque vous avez terminé.
BTW, j'aurais pu l'écrire comme ceci:
assert value == null || value.equals("Y") || value.equals("N") : value;
c'est très bien pour seulement trois valeurs, mais si le nombre de valeurs possibles devient plus grand, la version de HashSet devient plus commode. J'ai choisi la version HashSet pour faire mon point sur l'efficacité.
sont désactivées par défaut. Pour les activer, Nous devons exécuter le programme avec les options -ea
(granularité variable). Par exemple, java -ea AssertionsDemo
.
il existe deux formats d'utilisation des assertions:
- Simple: par exemple.
assert 1==2; // This will raise an AssertionError
. - mieux:
assert 1==2: "no way.. 1 is not equal to 2";
Cela va soulever une erreur D'assertion avec le message donné affiché et est donc mieux. Bien que la syntaxe actuelle soitassert expr1:expr2
là où expr2 peut être n'importe quelle expression retournant une valeur, je l'ai utilisé plus souvent juste pour imprimer un message.
pour récapituler (et cela est vrai pour de nombreuses langues pas seulement Java):
"assert" est principalement utilisé comme un débogage de l'aide par les développeurs de logiciels au cours du processus de débogage. Assert-les messages ne devraient jamais apparaître. De nombreuses langues offrent une option de compilation qui fera que toutes les "affirmations" seront ignorées, pour être utilisées dans la génération du code de "production".
"exceptions" sont un moyen pratique de gérer toutes sortes de conditions d'erreur, qu'ils représentent ou non des erreurs de logique, parce que, si vous tombez dans une condition d'erreur telle que vous ne pouvez pas continuer, vous pouvez simplement "les jeter en l'air," de l'endroit où vous êtes, s'attendant à ce que quelqu'un d'autre là-bas pour être prêt à les "attraper". Le contrôle est transféré en une seule étape, directement du code qui a lancé l'exception, directement au gant du receveur. (Et le receveur peut voir la trace des appels qui ont eu lieu.)
de plus, les appelants de ce sous-programme n'ont pas à vérifier si le sous-programme a réussi: "si nous sommes ici maintenant, il doit avoir réussi, parce que sinon il aurait jeté une exception et nous ne serions pas ici maintenant!" cette stratégie simple rend la conception de code et le débogage beaucoup, beaucoup plus facile.
Exceptions idéalement permettre fatale-conditions d'erreur d'être ce qu'ils sont: des "exceptions à la règle."Et, pour qu'ils soient manipulés par un code-chemin qui est aussi "une exception à la règle ... " fly ball!"
Assertion sont essentiellement utilisés pour déboguer la demande ou il est utilisé dans le remplacement de la manipulation d'exception pour une certaine application de vérifier la validité d'une demande.
"151900920 l'Assertion" fonctionne au moment de l'exécution. Un exemple simple, qui peut expliquer tout le concept très simplement, est ici - Que fait le mot-clé assert en Java? (WikiAnswers).en gros, "affirmer vrai " passera et" affirmer faux " échouera. Regardons comment cela va fonctionner:
public static void main(String[] args)
{
String s1 = "Hello";
assert checkInteger(s1);
}
private static boolean checkInteger(String s)
{
try {
Integer.parseInt(s);
return true;
}
catch(Exception e)
{
return false;
}
}
assert
est un mot clé. Il a été introduit dans JDK 1.4. Il y a deux types de assert
s
- très simple
assert
statements - Simple
assert
des déclarations.
par défaut, toutes les déclarations assert
ne seront pas exécutées. Si un énoncé assert
reçoit false, alors il soulèvera automatiquement une erreur d'assertion.