Que fait le mot-clé assert
dans Java?
Quels sont les exemples concrets pour comprendre le rôle clé des assertions?
Que fait le mot-clé assert
dans Java?
Quels sont les exemples concrets pour comprendre le rôle clé des assertions?
Les assertions (au moyen du mot-clé assert ) ont été ajoutées dans Java 1.4. Ils sont utilisés pour vérifier l'exactitude d'un invariant dans le code. Ils ne devraient jamais être déclenchés dans le code de production, et sont indicatifs d'un bug ou d'une mauvaise utilisation d'un chemin de code. Ils peuvent être activés lors de l' exécution par le -ea
option sur le java
commande, 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és écrire un programme pour le contrôle nucléaire à la centrale électrique. Il est assez évident que même la plus petite erreur peut avoir des résultats catastrophiques, par conséquent, votre code doit être exempt de bogues (en supposant que la JVM est sans bug pour le bien de l'argument).
Java n'est pas vérifiable, de la langue, ce qui signifie: vous ne pouvez pas calculer que le résultat de votre opération sera parfait. La raison principale de ceci sont des pointeurs: ils peuvent point partout ou nulle part, ils ne peuvent donc pas être calculé en fonction de cette valeur exacte, du moins pas dans un raisonnable de la durée 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 avez au moins trouver tous les bugs quand il arrive.
Cette idée est basée sur la Conception par Contrat (DbC) de paradigme: vous devez d'abord définir (avec mathématiquement précision) ce que votre méthode est censé faire, et puis le vérifier en testant cours d'exécution réel. Exemple:
// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
return a + b;
}
Tout cela est assez évident pour fonctionner correctement, la plupart des programmeurs ne verrez pas le bug caché à l'intérieur de celui-ci (indice: Ariane V s'est écrasé à cause d'un bug similaire). Maintenant, le DbC définit que vous devez toujours vérifier l'entrée et la sortie d'une fonction pour vérifier qu'il a fait un travail correct. Java permet de le faire assertions:
// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
assert (Integer.MAX - 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 tombe en panne, vous le remarquerez. Vous savez qu'il y a un problème dans votre code, vous savez où il est, et vous savez ce qu'il fait (semblables à des Exceptions). Et ce qui est encore plus important: vous arrêtez l'exécution de droite quand il arrive à empêcher la poursuite de code pour travailler avec des valeurs erronées et potentiellement causer des dommages à tout ce qu'il contrôle.
Java les Exceptions sont un concept similaire, mais ils ne parviennent pas à vérifier que tout. Si vous voulez encore plus de contrôles (au détriment de la vitesse d'exécution), vous devez utiliser les assertions. Cela permettra de gonfler votre code, mais vous pouvez à la fin de livrer un produit à un très court temps de développement (le plus tôt vous corriger un bug, plus le coût). Et en plus: si il y a un bug dans votre code, vous le détecter. Il n'existe aucun moyen d'un bug glisser à travers et causer des problèmes plus tard.
Ce n'est pas une garantie pour code sans bug, mais il est beaucoup plus proche de que les, que les programmes habituels.
Une fois, j'ai écrit une méthode qui compare deux valeurs arbitraires pour l'égalité, où la valeur peut ê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 l' equals()
méthode dans le cas où cettevaleur n'est pas nulle. Mais il suppose que l' equals()
méthode correctement remplit le contrat d' equals()
par correctement la manipulation d'un paramètre null.
Un collègue s'est objecté à mon code, en me disant que beaucoup de nos classes ont buggy equals()
méthodes qui n'ont pas fait de test pour les nuls, donc je devrais mettre cela de vérifier dans cette méthode. On peut se demander s'il est sage, ou si l'on doit forcer l'erreur, de sorte que nous pouvons repérer et de corriger, mais j'ai différé mon collègue et le mettre dans un null vérifier, 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); // null check
}
return result;
}
La vérification supplémentaire ici, other != null
, n'est nécessaire que si l' equals()
méthode ne parvient pas à vérifier la valeur null comme requis par son contrat.
Plutôt que de s'engager dans une polémique stérile avec mon collègue au sujet de la sagesse de laisser le code bogué séjour dans notre base de code, j'ai simplement mis deux affirmations contenues dans le code. Ces affirmations me permettra de savoir, au cours de la phase de développement, si l'un de nos classes ne parvient pas à mettre en oeuvre equals()
correctement, afin que je puisse corriger ça:
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 celles-ci:
Les affirmations sont le développement de la phase des outils uniquement.
Le point de l'affirmation est de vous permettre de savoir si il y a un bug, écrou simplement dans votre code, mais dans votre base de code. (Les assertions ici drapeau de bugs dans d'autres classes.)
Même si mon collègue était persuadé que nos classes ont été correctement écrit, les assertions ici serait toujours utile. De nouvelles classes seront ajoutées qui peuvent échouer pour tester la valeur null, et cette méthode peut signaler les bugs pour nous.
Dans le développement, vous devriez toujours se tourner affirmations, même si le code que vous avez écrit n'utilise pas des affirmations. Mon IDE est de toujours le faire par défaut pour tout nouvel exécutable.
Les assertions ne pas modifier le comportement du code en production, donc, mon collègue est heureux que la valeur null check est là, et que cette méthode permettra de s'exécuter correctement, même si l' equals()
méthode est buggé. Je suis content parce que je vais attraper toute buggy equals()
méthode en cours de développement.
Aussi, vous devez tester votre affirmation politique en mettant temporairement dans une affirmation qui ne manquera pas, de sorte que vous pouvez être certain que vous êtes informé, soit par le fichier journal ou une trace de la pile dans le flux de sortie.
Voici le cas d’utilisation plus courant. Supposons que vous passez sur une valeur enum :
Aussi longtemps que vous gérez tous les cas, vous êtes très bien. Mais un jour, quelqu'un va ajouter fig à votre enum et oubliez pas d’ajouter à votre instruction switch. Il produit un bug qui peut se complique attraper, parce que les effets ne se faire sentir jusqu'à après avoir quitté l’instruction switch. Mais si vous écrivez votre commutateur comme ceci, vous pouvez prendre immédiatement :
Les assertions sont utilisées pour vérifier les conditions préalables et ne doivent jamais échouer. Le code correct ne doit jamais échouer une assertion; quand ils se déclenchent, ils devraient indiquer un bug (heureusement à un endroit proche de l'emplacement du problème).
Un exemple d'assertion peut être de vérifier qu'un groupe particulier de méthodes est appelé dans le bon ordre (par exemple, que hasNext()
est appelé avant next()
dans un Iterator
).
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.