Que fait le mot clé Java assert et quand doit-il être utilisé?

602

Quels sont des exemples concrets pour comprendre le rôle clé des assertions?

Praveen
la source
8
Dans la vraie vie, vous ne les voyez presque jamais. Conjecture: si vous utilisez des assertions, vous devez penser à trois états: l'assertion réussit, l'assertion échoue, l'assertion est désactivée, au lieu de deux. Et l'assertion est désactivée par défaut, c'est donc l'état le plus probable, et il est difficile de s'assurer qu'il est activé pour votre code. Cela revient à dire que les assertions sont une optimisation prématurée qui serait d'une utilité limitée. Comme vous le voyez dans la réponse de @ Bjorn, il est même difficile de trouver un cas d'utilisation où vous ne voudriez pas échouer une assertion tout le temps.
Yishai
35
@Yishai: "vous devez penser à ... l'assertion est désactivée" Si vous avez besoin de faire cela, vous vous trompez. "les assertions sont une optimisation prématurée d'une utilisation limitée" . Voici le point de vue de Sun à ce sujet: " Utilisation des assertions dans la technologie Java " et c'est aussi bon à lire: " Les avantages de la programmation avec des assertions (alias assert déclarations) "
David Tonhofer
5
@DavidTonhofer, dans la vraie vie, vous ne les voyez presque jamais. C'est vérifiable. Vérifiez autant de projets open source que vous le souhaitez. Je ne dis pas que vous ne validez pas les invariants. Ce n'est pas la même chose. En d'autres termes. Si les assertions sont si importantes, pourquoi sont-elles désactivées par défaut?
Yishai
17
Une référence, FWIW: La relation entre les assertions logicielles et la qualité du code : "Nous comparons également l'efficacité des assertions à celle des techniques de recherche de bogues populaires comme les outils d'analyse statique du code source. Nous observons à partir de notre étude de cas qu'avec une augmentation de la densité d'assertions dans un fichier, il y a une diminution statistiquement significative de la densité des défauts. "
David Tonhofer
4
@DavidTonhofer David, je pense que votre amour pour l'affirmation est pour un type de programmation très spécifique que vous faites, dans mon domaine qui fonctionne avec des applications Web qui sortent du programme pour TOUTE raison est le plus grand NON NON - je n'ai personnellement jamais assert utilisé autre que les tests unitaires / intégraux
nightographe

Réponses:

426

Les assertions (via le 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 doivent jamais être déclenchés dans le code de production et indiquent un bogue ou une mauvaise utilisation d'un chemin de code. Ils peuvent être activés au moment de l'exécution au moyen de l' -eaoption sur lejava 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;
}
Ophidian
la source
71
En fait, Oracle vous dit de ne pas utiliser assertpour vérifier les paramètres des méthodes publiques ( docs.oracle.com/javase/1.4.2/docs/guide/lang/assert.html ). Cela devrait jeter un Exceptionau lieu de tuer le programme.
SJuan76
10
Mais vous n'expliquez toujours pas pourquoi ils existent. Pourquoi ne pouvez-vous pas faire une vérification if () et lever une exception?
El Mac
7
@ElMac - les assertions concernent les parties dev / debug / test du cycle - elles ne sont pas destinées à la production. Un bloc if s'exécute en prod. Les assertions simples ne ruineront pas la banque, mais les assertions coûteuses qui effectuent des validations de données complexes pourraient faire baisser votre environnement de production, c'est pourquoi elles y sont désactivées.
hoodaticus
2
@hoodaticus vous voulez dire uniquement que je peux activer / désactiver toutes les assertions pour le code prod est la raison? Parce que je peux quand même faire une validation de données complexe et ensuite la gérer avec des exceptions. Si j'ai du code de production, je pourrais désactiver les assertions complexes (et peut-être chères), car cela devrait fonctionner et a déjà été testé? En théorie, ils ne devraient pas faire baisser le programme car vous auriez de toute façon un problème.
El Mac
8
This convention is unaffected by the addition of the assert construct. Do not use assertions to check the parameters of a public method. An assert is inappropriate because the method guarantees that it will always enforce the argument checks. It must check its arguments whether or not assertions are enabled. Further, the assert construct does not throw an exception of the specified type. It can throw only an AssertionError. docs.oracle.com/javase/8/docs/technotes/guides/language/…
Bakhshi
325

Supposons que vous êtes censé écrire un programme pour contrôler une centrale nucléaire. Il est assez évident que même l'erreur la plus mineure pourrait avoir des résultats catastrophiques, donc votre code doit être exempt de bogues (en supposant que la JVM est exempte de bogues 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 raison principale en est les pointeurs: ils peuvent pointer n'importe où ou nulle part, donc ils ne peuvent pas être calculés pour avoir cette valeur exacte, du moins pas dans une plage de code raisonnable. Compte tenu de ce problème, il n'y a aucun moyen de prouver que votre code est correct dans son ensemble. Mais ce que vous pouvez faire, c'est de prouver que vous trouvez au moins tous les bogues quand cela se produit.

Cette idée est basée sur le paradigme de la conception par contrat (DbC): vous définissez d'abord (avec une précision mathématique) ce que votre méthode est censée faire, puis vous le vérifiez en le testant lors 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: 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'elle fonctionne correctement. Java peut le faire à travers des assertions:

// 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 maintenant, vous le remarquerez. Vous saurez qu'il y a un problème dans votre code, vous savez où il se trouve et vous savez ce qui l'a provoqué (similaire aux exceptions). Et ce qui est encore plus important: vous arrêtez de vous exécuter correctement quand cela arrive pour empêcher tout code supplémentaire de fonctionner avec des valeurs incorrectes et potentiellement endommager tout ce qu'il contrôle.

Les exceptions Java sont un concept similaire, mais elles ne parviennent pas à tout vérifier. Si vous voulez encore plus de contrôles (au prix de la vitesse d'exécution), vous devez utiliser des assertions. Cela gonflera votre code, mais vous pouvez finalement livrer un produit à un temps de développement étonnamment court (plus vous corrigez un bogue tôt, plus le coût est faible). Et en plus: s'il y a un bug dans votre code, vous le détecterez. Il n'y a aucun moyen de laisser passer un bogue et de provoquer des problèmes plus tard.

Ce n'est toujours pas une garantie pour un code sans bogue, mais c'est beaucoup plus proche de cela que les programmes habituels.

TwoThe
la source
29
J'ai choisi cet exemple car il présente très bien les bogues cachés dans un code apparemment sans bogue. Si cela ressemble à ce que quelqu'un d'autre a présenté, alors ils ont peut-être la même idée en tête. ;)
TwoThe
8
Vous choisissez assert car il échoue lorsque l'assertion est fausse. Un if peut avoir n'importe quel comportement. Frapper les cas marginaux est le travail des tests unitaires. L'utilisation de Design by Contract spécifie plutôt bien le contrat, mais comme pour les contrats réels, vous avez besoin d'un contrôle pour vous assurer qu'ils sont respectés. Avec les assertions, un chien de garde est inséré qui vous accompagnera lorsque le contrat n'est pas respecté. Considérez-le comme un avocat tenace hurlant "FAUX" chaque fois que vous faites quelque chose qui est en dehors ou contre un contrat que vous avez signé, puis vous renvoyez chez vous afin que vous ne puissiez pas continuer à travailler et à rompre le contrat!
Eric
5
Nécessaire dans ce cas simple: non, mais le DbC définit que chaque résultat doit être vérifié. Imaginez que quelqu'un modifie maintenant cette fonction en quelque chose de beaucoup plus complexe, puis il doit également adapter le post-contrôle, puis cela devient soudainement utile.
TwoLe
4
Désolé de ressusciter cela, mais j'ai une question spécifique. Quelle est la différence entre ce que @TwoThe a fait et au lieu d'utiliser assert simplement lancer un new IllegalArgumentExceptionavec le message? Je veux dire, en plus d'avoir o ajouter throwsà la déclaration de méthode et au code pour gérer cette exception ailleurs. Pourquoi assertinsetad de lancer une nouvelle exception? Ou pourquoi pas un ifau lieu de assert? Je ne peux pas vraiment obtenir ça :(
Blueriver
14
-1: L'affirmation de vérifier le débordement est fausse si elle apeut être négative. La deuxième affirmation est inutile; pour les valeurs int, il est toujours vrai que a + b - b == a. Ce test ne peut échouer que si l'ordinateur est fondamentalement cassé. Pour vous défendre contre cette éventualité, vous devez vérifier la cohérence entre plusieurs processeurs.
kevin cline
63

Les assertions sont un outil en phase de développement pour détecter les bogues dans votre code. Ils sont conçus pour être facilement supprimés, ils n'existeront donc pas dans le code de production. Les assertions ne font donc 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 consiste à tester null. De nombreuses 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 doit tout simplement jamais être nul. Donc, s'il est nul, il y a un bogue dans votre code que vous devez rechercher. Mais le code ci-dessus ne vous le dira jamais. Donc, dans un effort bien intentionné pour écrire du code "sûr", vous cachez également un bogue. Il vaut mieux é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 tôt. (Il est également utile de spécifier dans le contrat que ce paramètre ne doit jamais être nul.) Veillez à activer les assertions lorsque vous testez votre code pendant le développement. (Et persuader vos collègues de le faire aussi est souvent difficile, ce que je trouve très ennuyeux.)

Maintenant, certains de vos collègues s'opposeront à ce code, faisant valoir que vous devriez toujours mettre la vérification nulle pour empêcher une exception en production. Dans ce cas, l'assertion 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 contrôle nul soit là pour le code de production, mais pendant le développement, vous ne cachez plus le bogue lorsque le widget est nul.

Voici un exemple concret: j'ai écrit une fois une méthode qui comparait deux valeurs arbitraires pour l'égalité, où l'une ou l'autre valeur pouvait ê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 equals()méthode dans le cas où thisValue n'est pas null. Mais il suppose que la equals()méthode remplit correctement le contrat equals()en traitant correctement un paramètre nul.

Un collègue s'est opposé à mon code, me disant que beaucoup de nos classes ont des equals()méthodes de bug qui ne testent pas null, donc je devrais mettre cette vérification dans cette méthode. C'est discutable si c'est sage, ou si nous devons forcer l'erreur, afin que nous puissions la repérer et la corriger, mais j'ai remis à mon collègue et mis une vérification nulle, que j'ai marquée 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;
}

La vérification supplémentaire ici, other != nulln'est nécessaire que si la equals()méthode ne parvient pas à vérifier la valeur null comme requis par son contrat.

Plutôt que d'engager un débat infructueux avec mon collègue sur la sagesse de laisser le code buggy dans notre base de code, je mets simplement deux assertions dans le code. Ces assertions me permettront de savoir, pendant la phase de développement, si l'une de nos classes ne parvient pas à s'implémenter equals()correctement, donc je peux le 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:

  1. Les assertions ne sont que des outils de phase de développement.

  2. Le point d'une assertion est de vous faire savoir s'il y a un bug, pas seulement dans votre code, mais dans votre base de code . (Les assertions ici signaleront en fait des bogues dans d'autres classes.)

  3. Même si mon collègue était convaincu que nos cours étaient correctement écrits, les affirmations ici seraient toujours utiles. De nouvelles classes seront ajoutées qui pourraient échouer à tester pour null, et cette méthode peut signaler ces bogues pour nous.

  4. En cours de développement, vous devez toujours activer les assertions, même si le code que vous avez écrit n'utilise pas d'assertions. Mon IDE est configuré pour toujours le faire par défaut pour tout nouvel exécutable.

  5. Les assertions ne changent pas le comportement du code en 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 equals()méthode est boguée. Je suis content car j'attraperai n'importe quelle equals()méthode de buggy en développement.

En outre, vous devez tester votre stratégie d'assertion en insérant une assertion temporaire qui échouera, afin que vous puissiez être certain d'être averti, soit via le fichier journal, soit via une trace de pile dans le flux de sortie.

MiguelMunoz
la source
Bons points sur "cacher un bogue" et comment les affirmations exposent les bogues pendant le développement!
nobar
Aucun de ces contrôles n'est lent, il n'y a donc aucune raison de les désactiver en production. Ils doivent être convertis en instructions de journalisation, afin que vous puissiez détecter les problèmes qui n'apparaissent pas dans votre «phase de développement». (Vraiment, il n'y a rien de tel qu'une phase de développement, de toute façon. Le développement se termine lorsque vous décidez d'arrêter de maintenir votre code du tout.)
Aleksandr Dubinsky
20

Beaucoup de bonnes réponses expliquant ce que fait le assertmot-clé, mais peu de réponses à la vraie question, "quand le assertmot - clé devrait -il être utilisé dans la vraie vie?"

La réponse: presque jamais .

Les affirmations, en tant que concept, sont merveilleuses. Le bon code a beaucoup de if (...) throw ...déclarations (et leurs proches comme Objects.requireNonNullet Math.addExact). Cependant, certaines décisions de conception ont considérablement limité l'utilité du mot - assert clé - lui même.

L'idée motrice derrière le assertmot-clé est l'optimisation prématurée, et la principale caractéristique est de pouvoir facilement désactiver toutes les vérifications. En fait, leassert contrôles sont désactivés par défaut.

Cependant, il est extrêmement important que les contrôles invariants continuent d'être effectués en production. En effet, une couverture de test parfaite est impossible et tout le code de production comportera des bogues que les 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.

Parfois, on pourrait être tenté d'écrire un chèque invariant qui prend un temps excessivement long à traiter (et qui est appelé assez souvent pour que cela ait de l'importance). Cependant, ces vérifications ralentiront les tests, ce qui est également indésirable. Ces vérifications chronophages sont généralement écrites sous forme de tests unitaires. Néanmoins, il peut parfois être judicieux d'utiliserassert pour cette raison.

Ne l'utilisez pas assertsimplement parce qu'il est plus propre et plus joli que if (...) throw ...(et je le dis avec beaucoup de peine, car j'aime bien propre et joli). Si vous ne pouvez pas vous aider vous-même et pouvez contrôler le lancement de votre application, n'hésitez pas à l'utiliser, assertmais activez toujours les assertions en production. Certes, c'est ce que j'ai tendance à faire. Je pousse pour une annotation lombok qui fera assertagir plus comme if (...) throw .... Votez pour ça ici.

(Rant: les développeurs JVM étaient un tas de codeurs horribles et optimisés prématurément. C'est pourquoi vous entendez parler de tant de problèmes de sécurité dans le plugin Java et la JVM. Ils ont refusé d'inclure des vérifications et des assertions de base dans le code de production, et nous continuons à payer le prix.)

Aleksandr Dubinsky
la source
2
@aberglas Une clause fourre-tout est catch (Throwable t). Il n'y a aucune raison de ne pas essayer d'intercepter, de journaliser ou de réessayer / récupérer de OutOfMemoryError, AssertionError, etc.
Aleksandr Dubinsky
1
J'ai attrapé et récupéré d'OutOfMemoryError.
MiguelMunoz
1
Je ne suis pas d'accord. Beaucoup de mes assertions sont utilisées pour s'assurer que mon API est correctement appelée. Par exemple, je pourrais écrire une méthode privée qui ne devrait être appelée que lorsqu'un objet contient un verrou. Si un autre développeur appelle cette méthode à partir d'une partie du code qui ne verrouille pas l'objet, l'assertion lui dira immédiatement qu'il a fait une erreur. Il y a beaucoup d'erreurs comme celle-ci qui peuvent, avec certitude, se coincer dans la phase de développement, et les affirmations sont très utiles dans ces cas.
MiguelMunoz
2
@MiguelMunoz Dans ma réponse, j'ai dit que l'idée d'assertions était très bonne. C'est l'implémentation du assertmot-clé qui est mauvaise. Je vais modifier ma réponse pour qu'il soit plus clair que je fais référence au mot-clé, pas au concept.
Aleksandr Dubinsky
2
J'aime le fait qu'il lève une AssertionError au lieu d'une Exception. Trop de développeurs n'ont toujours pas appris qu'ils ne devraient pas intercepter l'exception si le code ne peut lancer que quelque chose comme IOException. J'ai eu des bogues dans mon code complètement avalés parce que quelqu'un a attrapé Exception. Les assertions ne sont pas prises dans ce piège. Les exceptions concernent les situations que vous attendez à voir dans le code de production. En ce qui concerne la journalisation, vous devez également consigner toutes vos erreurs, même si les erreurs sont rares. Par exemple, voulez-vous vraiment laisser passer une OutOfMemoryError sans la journaliser?
MiguelMunoz
14

Voici le cas d'utilisation le plus courant. Supposons que vous allumiez une valeur énumérée:

switch (fruit) {
  case apple:
    // do something
    break;
  case pear:
    // do something
    break;
  case banana:
    // do something
    break;
}

Tant que vous gérez chaque cas, tout va bien. Mais un jour, quelqu'un ajoutera de la figue à votre énumération et oubliera de l'ajouter à votre instruction switch. Cela produit un bogue qui peut être difficile à détecter, car les effets ne se feront sentir qu'après avoir quitté l'instruction switch. Mais si vous écrivez votre commutateur 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;
}
MiguelMunoz
la source
4
C'est pourquoi vous devez activer les avertissements et les avertissements traités comme des erreurs. Tout compilateur décent à mi-chemin est capable de vous dire, si seulement vous le permettez de vous le dire, que vous manquez une vérification d'énumération, et il le fera au moment de la compilation, ce qui est incroyablement mieux que (peut-être un jour) le découvrir à Durée.
Mike Nakis
9
pourquoi utiliser une assertion ici plutôt qu'une exception quelconque, par exemple, une exception d'argument illégale?
liltitus27
4
Cela lancera un AssertionErrorsi les assertions sont activées ( -ea). Quel est le comportement souhaité en production? Un no-op silencieux et un désastre potentiel plus tard dans l'exécution? Probablement pas. Je proposerais une explicite throw new AssertionError("Missing enum value: " + fruit);.
aioobe
1
Il y a un bon argument à faire pour simplement lancer une AssertionError. En ce qui concerne le bon comportement en production, le point essentiel des affirmations est d'empêcher que cela ne se produise en production. Les assertions sont un outil de phase de développement pour détecter les bogues, qui peuvent facilement être supprimés du code de production. Dans ce cas, il n'y a aucune raison de le supprimer du code de production. Mais dans de nombreux cas, les tests d'intégrité peuvent ralentir les choses. En mettant ces tests à l'intérieur d'assertions, qui ne sont pas utilisées dans le code de production, vous êtes libre d'écrire des tests approfondis, sans craindre qu'ils ne ralentissent votre code de production.
MiguelMunoz
Cela semble être faux. À mon humble avis, vous ne devez pas utiliser defaultpour que le compilateur puisse vous avertir des cas manquants. Vous pouvez returnau lieu de break(cela peut nécessiter l'extraction de la méthode), puis gérer le cas manquant après le commutateur. De cette façon, vous obtenez à la fois l'avertissement et la possibilité de le faire assert.
maaartinus
12

Les assertions sont utilisées pour vérifier les conditions postérieures et les conditions préalables «ne devraient jamais échouer». Un code correct ne doit jamais échouer une assertion; lorsqu'ils se déclenchent, ils doivent indiquer un bug (si tout va bien à un endroit qui est proche de l'endroit où se trouve le problème).

Un exemple d'assertion pourrait être de vérifier qu'un groupe particulier de méthodes est appelé dans le bon ordre (par exemple, qui hasNext()est appelé avant next()dans un Iterator).

Associés Donal
la source
1
Vous n'avez pas besoin d'appeler hasNext () avant next ().
DJClayworth
6
@DJClayworth: Vous n'avez pas non plus à éviter de déclencher des assertions. :-)
Donal Fellows
8

Que fait le mot clé assert en Java?

Regardons le bytecode compilé.

Nous conclurons que:

public class Assert {
    public static void main(String[] args) {
        assert System.currentTimeMillis() == 0L;
    }
}

génère presque exactement 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();
            }
        }
    }
}

Assert.class.desiredAssertionStatus()est truequand -eaest passé sur la ligne de commande, et false sinon.

Nous utilisons System.currentTimeMillis()pour nous assurer qu'il ne sera pas optimisé ( 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 du chargement, puis il met en cache le résultat. Voir aussi: Quelle est la signification de "synthétique statique"?

Nous pouvons vérifier cela avec:

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
  • assertpourrait être assez bien émulé avec des propriétés système -Pcom.me.assert=trueà remplacer -easur la ligne de commande, et a throw new AssertionError().
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
2
Donc, la catch (Throwable t)clause peut également détecter les violations d'assertion? Pour moi, cela limite leur utilité uniquement au cas où le corps de l'assertion prend du temps, ce qui est rare.
Evgeni Sergeev
1
Je ne sais pas pourquoi cela limite l'utilité de l'assertion. Vous ne devriez jamais attraper un Throwable sauf dans de très rares cas. Si vous avez besoin d'attraper Throwable mais que vous ne voulez pas qu'il attrape les assertions, vous pouvez simplement attraper la AssertionErrorpremière et la relancer.
MiguelMunoz
7

Un exemple du monde réel, à partir d'une classe Stack (à partir de l' assertion dans les articles Java )

public int pop() {
   // precondition
   assert !isEmpty() : "Stack is empty";
   return stack[--num];
}
Björn
la source
80
Cela serait mal vu dans C: Une assertion est quelque chose qui ne devrait VRAIMENT JAMAIS se produire - éclater une pile vide devrait lever une NoElementsException ou quelque chose du genre. Voir la réponse de Donal.
Konerak
4
Je suis d'accord. Même si cela est tiré d'un tutoriel officiel, c'est un mauvais exemple.
DJClayworth
7
Il y a probablement une fuite de mémoire là-bas. Vous devez définir stack [num] = null; afin que le GC fasse son travail correctement.
H.Rabiee
3
Je pense que dans une méthode privée, il serait correct d'utiliser une assertion, car il serait bizarre d'avoir des exceptions pour un dysfonctionnement d'une classe ou d'une méthode. Dans une méthode publique, en l'appelant de quelque part à l'extérieur, vous ne pouvez pas vraiment dire comment l'autre code l'utilise. Vérifie-t-il vraiment isEmpty () ou non? Tu ne sais pas.
Vlasec
7

Une assertion permet de détecter des défauts dans le code. Vous pouvez activer les assertions pour les tests et le débogage tout en les désactivant 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, ce n'est peut-être pas vrai. Détecter cela plus tôt dans le processus vous permet de savoir que quelque chose ne va pas.

Une assertinstruction contient cette instruction avec un Stringmessage facultatif .

La syntaxe d'une instruction assert 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 doivent être utilisées pour:

  1. Validation des paramètres d'entrée d'une méthode privée. PAS pour les méthodes publiques. publicles méthodes doivent lever des exceptions régulières lorsqu'elles sont passées de mauvais paramètres.

  2. N'importe où dans le programme pour garantir la validité d'un fait qui est presque certainement vrai.

Par exemple, si vous êtes sûr qu'il ne s'agira que de 1 ou 2, vous pouvez utiliser une assertion comme celle-ci:

...
if (i == 1)    {
    ...
}
else if (i == 2)    {
    ...
} else {
    assert false : "cannot happen. i is " + i;
}
...
  1. Validation des conditions de publication à la fin de toute méthode. Cela signifie qu'après avoir exécuté la logique métier, vous pouvez utiliser des assertions pour vous assurer que l'état interne de vos variables ou résultats est conforme à ce que vous attendez. Par exemple, une méthode qui ouvre un socket ou un fichier peut utiliser une assertion à la fin pour garantir que le socket ou le fichier est bien ouvert.

Les assertions ne doivent pas être utilisées pour:

  1. Validation des paramètres d'entrée d'une méthode publique. Comme les assertions ne sont pas toujours exécutées, le mécanisme d'exception normal doit être utilisé.

  2. Validation des contraintes sur quelque chose qui est entré par l'utilisateur. Comme ci-dessus.

  3. Ne doit pas être utilisé pour des effets secondaires.

Par exemple, ce n'est pas une utilisation appropriée car ici l'assertion est utilisée pour son effet secondaire d'appeler la doSomething()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");
}
solomkinmv
la source
5

En plus de toutes les bonnes réponses fournies ici, le guide de programmation officiel de Java SE 7 contient un manuel assez concis sur l'utilisation assert; avec plusieurs exemples précis sur le moment où il est bon (et, surtout, mauvais) d'utiliser des assertions, et en quoi c'est différent de lever des exceptions.

Lien

Ivan Bartsov
la source
1
Je suis d'accord. L'article contient de nombreux excellents exemples. J'ai particulièrement aimé celui de s'assurer qu'une méthode n'est appelée que lorsque l'objet contient un verrou.
MiguelMunoz
4

Assert est très utile lors du développement. Vous l'utilisez quand quelque chose ne peut tout simplement pas se produire si votre code fonctionne correctement. Il est facile à utiliser et peut rester dans le code pour toujours, car il sera désactivé dans la vraie vie.

S'il y a une chance que la condition puisse se produire dans la vraie vie, vous devez la gérer.

Je l'adore, mais je ne sais pas comment l'activer dans Eclipse / Android / ADT. Il semble être désactivé même lors du débogage. (Il y a un fil à ce sujet, mais il fait référence au «Java vm», qui n'apparaît pas dans la configuration d'exécution ADT).

John White
la source
1
Pour activer l'assertion dans l'éclipse IDE, veuillez suivre tutoringcenter.cs.usfca.edu/resources/…
Ayaz Pasha
Je ne pense pas qu'il existe un moyen d'activer les assertions dans Android. C'est très décevant.
MiguelMunoz
3

Voici une assertion que j'ai écrite sur un serveur pour un projet Hibernate / SQL. Un bean entité avait deux propriétés effectivement booléennes, appelées isActive et isDefault. Chacun pouvait avoir une valeur "Y" ou "N" ou null, qui était traité comme "N". Nous voulons nous assurer que le client du navigateur est limité à ces trois valeurs. Donc, 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;

Remarquez ce qui suit.

  1. Cette assertion concerne uniquement la phase de développement. Si le client envoie une mauvaise valeur, nous l'attraperons tôt et le réparerons, bien avant d'atteindre la production. Les affirmations concernent les défauts que vous pouvez détecter tôt.

  2. Cette affirmation est lente et inefficace. C'est bon. Les assertions peuvent être lentes. Nous ne nous en soucions pas car ce sont des outils de développement uniquement. Cela ne ralentira pas le code de production car les assertions seront désactivées. (Il y a un certain désaccord sur ce point, que j'aborderai plus tard.) Cela m'amène à mon prochain point.

  3. Cette affirmation n'a aucun effet secondaire. J'aurais pu tester ma valeur par rapport à un ensemble final statique non modifiable, mais cet ensemble serait resté en production, où il ne serait jamais utilisé.

  4. Cette assertion existe pour vérifier le bon fonctionnement du client. Ainsi, au moment où nous atteindrons la production, nous serons sûrs que le client fonctionne correctement, afin que nous puissions désactiver l'assertion en toute sécurité.

  5. Certaines personnes demandent ceci: si l'assertion n'est pas nécessaire en production, pourquoi ne pas simplement les retirer lorsque vous avez terminé? Parce que vous en aurez toujours besoin lorsque vous commencerez à travailler sur la prochaine version.

Certaines personnes ont fait valoir que vous ne devriez jamais utiliser d'assertions, car vous ne pouvez jamais être sûr que tous les bogues ont disparu, vous devez donc les garder même en production. Et il est donc inutile d'utiliser l'instruction assert, car le seul avantage des assertions est que vous pouvez les désactiver. Par conséquent, selon cette réflexion, vous ne devriez (presque) jamais utiliser d'assertions. Je ne suis pas d'accord. Il est certainement vrai que si un test appartient à la production, vous ne devez pas utiliser d'assertion. Mais ce test ne pas à la production. Celui-ci sert à attraper un bogue qui n'atteindra probablement jamais la production, il peut donc être désactivé 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 bien pour seulement trois valeurs, mais si le nombre de valeurs possibles augmente, la version HashSet devient plus pratique. J'ai choisi la version HashSet pour faire valoir mon point de vue sur l'efficacité.

MiguelMunoz
la source
Je doute fortement que l'utilisation d'un si petit a HashSetapporte un avantage de vitesse sur un ArrayList. De plus, les créations d'ensemble et de liste dominent le temps de recherche. Ils iraient bien en utilisant une constante. Cela dit, +1.
maaartinus
C'est vrai. Je l'ai fait de cette manière inefficace pour illustrer mon point de vue selon lequel les affirmations peuvent être lentes. Celui-ci pourrait être rendu plus efficace, mais il y en a d'autres qui ne le peuvent pas. Dans un excellent livre intitulé "Writing Solid Code", Steve Maguire parle d'une assertion dans Microsoft Excel pour tester le nouveau code de mise à jour incrémentielle qui a ignoré les cellules qui ne devraient pas changer. Chaque fois que l'utilisateur apportait une modification, l'assertion recalculait la feuille de calcul entière pour s'assurer que les résultats correspondaient à ceux de la fonctionnalité de mise à jour incrémentielle. Cela a vraiment ralenti la version de débogage, mais ils ont détecté tous leurs bogues tôt.
MiguelMunoz
Entièrement d'accord. Les assertions sont en quelque sorte des tests - elles sont moins polyvalentes que les tests ordinaires, mais elles peuvent couvrir des méthodes privées et elles sont beaucoup moins chères à écrire. Je vais essayer de les utiliser encore plus.
maaartinus
2

Les assertions sont essentiellement utilisées pour déboguer l'application ou elles sont utilisées en remplacement de la gestion des exceptions pour certaines applications afin de vérifier la validité d'une application.

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).

SBTec
la source
2

Les assertions sont désactivées par défaut. Pour les activer, nous devons exécuter le programme avec des -eaoptions (la granularité peut varier). Par exemple java -ea AssertionsDemo,.

Il existe deux formats d'utilisation des assertions:

  1. Simple: par exemple. assert 1==2; // This will raise an AssertionError.
  2. Mieux: assert 1==2: "no way.. 1 is not equal to 2"; Cela soulèvera une AssertionError avec le message donné affiché aussi et c'est donc mieux. Bien que la syntaxe réelle soit assert expr1:expr2où expr2 peut être n'importe quelle expression renvoyant une valeur, je l'ai utilisée plus souvent juste pour imprimer un message.
Chandan Purohit
la source
1

Pour récapituler (et cela est vrai de nombreux langages, pas seulement Java):

"assert" est principalement utilisé comme aide au débogage par les développeurs de logiciels pendant le processus de débogage. Les messages d'assertion ne doivent jamais apparaître. De nombreux langages offrent une option au moment de la compilation qui entraînera l'ignorance de toutes les «assertions», pour une utilisation lors de la génération de code de «production».

les "exceptions" sont un moyen pratique de gérer toutes sortes de conditions d'erreur, qu'elles représentent ou non des erreurs logiques, car, si vous rencontrez une condition d'erreur telle que vous ne pouvez pas continuer, vous pouvez simplement "les jeter en l'air", "où que vous soyez, en attendant que quelqu'un d'autre soit prêt à les" attraper ". Le contrôle est transféré en une seule étape, directement du code qui a levé l'exception, directement au gant du receveur. (Et le receveur peut voir la trace complète 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, car sinon il aurait levé une exception et nous ne serions pas ici maintenant!" Cette stratégie simple rend la conception de code et le débogage beaucoup plus faciles.

Les exceptions permettent aux conditions d'erreur fatale d'être ce qu'elles sont: «des exceptions à la règle». Et, pour qu'ils soient gérés par un chemin de code qui est aussi "une exception à la règle ... " fly ball! "

Mike Robinson
la source
1

Les assertions sont des contrôles qui peuvent être désactivés. Ils sont rarement utilisés. Pourquoi?

  • Ils ne doivent pas être utilisés pour vérifier les arguments des méthodes publiques car vous n'avez aucun contrôle sur eux.
  • Ils ne doivent pas être utilisés pour des contrôles simples, result != nullcar ces contrôles sont très rapides et il n'y a pratiquement rien à enregistrer.

Alors, que reste-t-il? Des vérifications coûteuses des conditions devraient être vraies. Un bon exemple serait les invariants d'une structure de données comme RB-tree. En fait, dans ConcurrentHashMapJDK8, il y a quelques assertions significatives pour le TreeNodes.

  • Vous ne voulez vraiment pas les activer en production car ils pourraient facilement dominer le temps d'exécution.
  • Vous souhaiterez peut-être les activer ou désactiver pendant les tests.
  • Vous devez absolument les activer lorsque vous travaillez sur le code.

Parfois, le chèque n'est pas vraiment cher, mais en même temps, vous êtes presque sûr qu'il passera. Dans mon code, il y a par exemple,

assert Sets.newHashSet(userIds).size() == userIds.size();

où je suis presque sûr que la liste que je viens de créer a des éléments uniques, mais je voulais documenter et revérifier.

maaartinus
la source
0

Fondamentalement, «affirmer vrai» passera et «affirmera faux» échouera. Voyons comment cela fonctionnera:

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;
    }
}
Peter Mortensen
la source
-8

assertest un mot-clé. Il a été introduit dans JDK 1.4. Il existe deux types de asserts

  1. Très simples assertdéclarations
  2. Déclarations simples assert.

Par défaut, toutes les assertinstructions ne seront pas exécutées. Si une assertinstruction reçoit false, elle déclenchera automatiquement une erreur d'assertion.

pavani
la source
1
Il ne fournit aucun exemple
concret
1
Vous venez de copier-coller depuis: amazon.com/Programmer-Study-1Z0-803-1Z0-804-Certification/dp/… ?
Koray Tugay