Comment utiliser les assertions en Java

L'écriture de programmes qui fonctionnent correctement au moment de l'exécution peut être difficile. En effet, nos hypothèses sur le comportement de notre code lors de son exécution sont souvent fausses. L'utilisation de la fonction d'assertions de Java est un moyen de vérifier que votre logique de programmation est correcte.

Ce didacticiel présente les assertions Java. Vous apprendrez d'abord ce que sont les assertions et comment les spécifier et les utiliser dans votre code. Ensuite, vous découvrirez comment utiliser les assertions pour appliquer les préconditions et les postconditions. Enfin, vous comparerez les assertions avec les exceptions et découvrirez pourquoi vous avez besoin des deux dans votre code.

télécharger Obtenir le code Téléchargez le code source pour des exemples dans ce didacticiel. Créé par Jeff Friesen pour JavaWorld.

Que sont les assertions Java?

Avant JDK 1.4, les développeurs utilisaient souvent des commentaires pour documenter les hypothèses sur l'exactitude des programmes. Les commentaires sont cependant inutiles en tant que mécanisme de test et de débogage des hypothèses. Le compilateur ignore les commentaires, il n'y a donc aucun moyen de les utiliser pour la détection de bogues. Les développeurs ne mettent souvent pas non plus à jour les commentaires lorsqu'ils changent de code.  

Dans JDK 1.4, les assertions ont été introduites comme nouveau mécanisme de test et de débogage des hypothèses concernant notre code. En substance, les assertions  sont des entités compilables qui s'exécutent au moment de l'exécution, en supposant que vous les ayez activées pour les tests de programmes. Vous pouvez programmer des assertions pour vous informer des bogues où les bogues se produisent, ce qui réduit considérablement le temps que vous passeriez autrement à déboguer un programme défaillant.

Les assertions sont utilisées pour codifier les exigences qui rendent un programme correct ou non en testant des conditions (expressions booléennes) pour des valeurs vraies, et en notifiant le développeur lorsque ces conditions sont fausses. L'utilisation d'assertions peut considérablement augmenter votre confiance dans l'exactitude de votre code.

Comment écrire une assertion en Java

Les assertions sont implémentées via l' assertinstruction et la java.lang.AssertionErrorclasse. Cette instruction commence par le mot-clé assertet se poursuit avec une expression booléenne. Il s'exprime syntaxiquement comme suit:

assert BooleanExpr ;

Si la valeur est BooleanExprtrue, rien ne se passe et l'exécution se poursuit. Cependant, si l'expression a la valeur false, elle AssertionErrorest instanciée et lancée, comme illustré dans le Listing 1.

Liste 1:AssertDemo.java (version 1)

classe publique AssertDemo {public static void main (String [] args) {int x = -1; affirmer x> = 0; }}

L'assertion du Listing 1 indique que le développeur croit que la variable xcontient une valeur supérieure ou égale à 0. Cependant, ce n'est clairement pas le cas; l' assertexécution de l' instruction entraîne un jeté AssertionError.

Compilez le listing 1 ( javac AssertDemo.java) et exécutez-le avec les assertions activées ( java -ea AssertDemo). Vous devez observer la sortie suivante:

Exception dans le thread "main" java.lang.AssertionError à AssertDemo.main (AssertDemo.java:6)

Ce message est quelque peu cryptique en ce sens qu'il n'identifie pas ce qui a provoqué le AssertionErrorlancement du. Si vous souhaitez un message plus informatif, utilisez la assertdéclaration exprimée ci-dessous:

assert BooleanExpr : expr ;

Voici exprtoute expression (y compris un appel de méthode) qui peut renvoyer une valeur - vous ne pouvez pas appeler une méthode avec un voidtype de retour. Une expression utile est une chaîne littérale qui décrit la raison de l'échec, comme illustré dans le Listing 2.

Listing 2:AssertDemo.java (version 2)

classe publique AssertDemo {public static void main (String [] args) {int x = -1; assert x> = 0: "x <0"; }}

Compilez le listing 2 ( javac AssertDemo.java) et exécutez-le avec les assertions activées ( java -ea AssertDemo). Cette fois, vous devriez observer la sortie légèrement développée suivante, qui inclut la raison du levé AssertionError:

Exception dans le thread "main" java.lang.AssertionError: x <0 sur AssertDemo.main (AssertDemo.java:6)

Dans les deux cas, l'exécution AssertDemosans l' -eaoption (activer les assertions) n'entraîne aucune sortie. Lorsque les assertions ne sont pas activées, elles ne sont pas exécutées, bien qu'elles soient toujours présentes dans le fichier de classe.

Conditions préalables et postconditions

Les assertions testent les hypothèses d'un programme en vérifiant que ses différentes conditions préalables et postconditions ne sont pas violées, alertant le développeur lorsqu'une violation se produit:

  • Une condition préalable est une condition qui doit être évaluée à true avant l'exécution d'une séquence de code. Les conditions préalables garantissent que les appelants respectent leurs contrats avec les appelants.
  • Une postcondition est une condition qui doit être évaluée à true après l'exécution d'une séquence de code. Les postconditions garantissent que les appelants respectent leurs contrats avec les appelants.

Conditions préalables

Vous pouvez appliquer des conditions préalables aux constructeurs et méthodes publics en effectuant des vérifications explicites et en lançant des exceptions si nécessaire. Pour les méthodes d'assistance privées, vous pouvez appliquer des conditions préalables en spécifiant des assertions. Considérez la liste 3.

Listing 3:AssertDemo.java (version 3)

import java.io.FileInputStream; import java.io.InputStream; import java.io.IOException; class PNG {/ ** * Créer une instance PNG, lire le fichier PNG spécifié et le décoder * en structures appropriées. * * @param filespec chemin et nom du fichier PNG à lire * * @throws NullPointerException quand filespecest *null* / PNG (String filespec) jette IOException {// Appliquer les conditions préalables dans les constructeurs et // méthodes non privés. if (filespec == null) throw new NullPointerException ("filespec is null"); try (FileInputStream fis = new FileInputStream (filespec)) {readHeader (fis); }} private void readHeader (InputStream is) throws IOException {// Confirmez que la précondition est satisfaite dans les // méthodes d'assistance privées. assert is! = null: "nul passé à est"; }} classe publique AssertDemo {public static void main (String [] args) lance IOException {PNG png = new PNG ((args.length == 0)? null: args [0]); }}

La PNGclasse du Listing 3 est le début minimal d'une bibliothèque pour la lecture et le décodage de fichiers image PNG (graphiques de réseau portables). Le constructeur compare explicitement filespecavec null, lançant NullPointerExceptionlorsque ce paramètre contient null. Le point est d'appliquer la condition préalable qui filespecne contient pas null.

Il n'est pas approprié de spécifier assert filespec != null;parce que la condition préalable mentionnée dans Javadoc du constructeur ne serait pas (techniquement) honorée lorsque les assertions étaient désactivées. (En fait, ce serait honoré parce FileInputStream()que jetterait NullPointerException, mais vous ne devriez pas dépendre d'un comportement non documenté.)

Cependant, assertest approprié dans le contexte de la readHeader()méthode d'assistance privée , qui sera éventuellement complétée pour lire et décoder l'en-tête de 8 octets d'un fichier PNG. La condition préalable à laquelle isune valeur non nulle est toujours passée sera toujours valable.

Postconditions

Les postconditions sont généralement spécifiées via des assertions, que la méthode (ou le constructeur) soit publique ou non. Considérez le Listing 4.

Listing 4:AssertDemo.java (version 4)

public class AssertDemo { public static void main(String[] args) { int[] array = { 20, 91, -6, 16, 0, 7, 51, 42, 3, 1 }; sort(array); for (int element: array) System.out.printf("%d ", element); System.out.println(); } private static boolean isSorted(int[] x) { for (int i = 0; i  x[i + 1]) return false; return true; } private static void sort(int[] x) { int j, a; // For all integer values except the leftmost value ... for (int i = 1; i  0 && x[j - 1] > a) { // Shift left value -- x[j - 1] -- one position to its right -- // x[j]. x[j] = x[j - 1]; // Update insert position to shifted value's original position // (one position to the left). j--; } // Insert a at insert position (which is either the initial insert // position or the final insert position), where a is greater than // or equal to all values to its left. x[j] = a; } assert isSorted(x): "array not sorted"; } }

Listing 4 presents a sort() helper method that uses the insertion sort algorithm to sort an array of integer values. I’ve used assert to check the postcondition of x being sorted before sort() returns to its caller.

The example in Listing 4 demonstrates an important characteristic of assertions, which is that they’re typically expensive to execute. For this reason, assertions are usually disabled in production code. In Listing 4, isSorted() must scan through the entire array, which can be time-consuming in the case of a lengthy array.

Assertions vs. exceptions in Java

Developers use assertions to document logically impossible situations and detect errors in their programming logic. At runtime, an enabled assertion alerts a developer to a logic error. The developer refactors the source code to fix the logic error and then recompiles this code.

Developers use Java’s exception mechanism to respond to non-fatal (e.g., running out of memory) runtime errors, which may be caused by environmental factors, such as a file not existing, or by poorly written code, such as an attempt to divide by 0. An exception handler is often written to gracefully recover from the error so that the program can continue to run.

Assertions are no substitute for exceptions. Unlike exceptions, assertions don’t support error recovery (assertions typically halt program execution immediately — AssertionError isn’t meant to be caught); they are often disabled in production code; and they typically don’t display user-friendly error messages (although this isn’t an issue with assert). It’s important to know when to use exceptions rather than assertions.

When to use exceptions

Suppose you’ve written a sqrt() method that calculates the square root of its argument. In a non-complex number context, it’s impossible to take the square root of a negative number. Therefore, you use an assertion to fail the method if the argument is negative. Consider the following code fragment:

public double sqrt(double x) { assert x >= 0 : "x is negative"; // ... }

It’s inappropriate to use an assertion to validate an argument in this public method. An assertion is intended to detect errors in programming logic and not to safeguard a method from erroneous arguments. Besides, if assertions are disabled, there is no way to deal with the problem of a negative argument. It’s better to throw an exception, as follows:

public double sqrt(double x) { if (x < 0) throw new IllegalArgumentException("x is negative"); // ... }

The developer might choose to have the program handle the illegal argument exception, or simply propagate it out of the program where an error message is displayed by the tool that runs the program. Upon reading the error message, the developer can fix whatever code led to the exception.

Vous avez peut-être remarqué une différence subtile entre l'assertion et la logique de détection d'erreur. Les tests d'assertion x >= 0, tandis que les tests de logique de détection d'erreur x < 0. L'assertion est optimiste: nous supposons que l'argument est OK. En revanche, la logique de détection d'erreur est pessimiste: nous supposons que l'argument n'est pas OK. Les assertions documentent une logique correcte, tandis que les exceptions documentent un comportement d'exécution incorrect.

Dans ce didacticiel, vous avez appris à utiliser des assertions pour documenter une logique de programme correcte. Vous avez également appris pourquoi les assertions ne remplacent pas les exceptions, et vous avez vu un exemple où l'utilisation d'une exception serait plus efficace.

Cette histoire, "Comment utiliser les assertions en Java" a été publiée à l'origine par JavaWorld.