1
votes

Pourquoi Throwable.getCause vérifie-t-il si «cause» est «this» avant de renvoyer «null» au lieu de simplement renvoyer directement la cause?

J'avais une raison d'aller dans le code source de Throwable.getCause aujourd'hui et Throwable.getCause été un peu surpris de voir ce code dans cette méthode:

    public synchronized Throwable getCause() {
        return (cause==this ? null : cause);
    }

Cela vient de Java 1.8, mais cela a la même apparence dans les versions ultérieures que j'ai examinées.

Ma question est la suivante: pourquoi ne pas simplement return cause et en finir?


0 commentaires

3 Réponses :


2
votes

Il ne compare pas cause à null , il compare cause à this . Pour éviter les cercles, si la cause est la this , il renvoie null.


3 commentaires

OMG duh. Je viens de lire cette façon de code trop vite.


Comme la question posée à l'origine est manifestement stupide, j'ai pensé à la supprimer. Au lieu de cela, je pense que je modifierai la question pour la rendre plus utile à quiconque pourrait la lire plus tard.


Je vais attribuer la "réponse" à @Persixty car cette réponse explique pourquoi cela est fait, même si bmargulies a répondu correctement à la question d'origine. La réponse de Persixty a une explication complète de la raison de cette vérification.



1
votes

Cette valeur null est utilisée par la méthode printStackTrace , par exemple lors de l'appel de la méthode stackTraceString , lignes 421..450 pour identifier quand la sortie de la trace de pile doit être terminée.


0 commentaires

2
votes

Le code:

public Throwable initCause(Throwable cause) {
    if (cause == this) //Illogical! An exception can't be self caused!
            throw new IllegalArgumentException();
    if (this.cause != this)// false if cause has been initialised 'properly'.
        throw new IllegalStateException();
    this.cause = cause;
     return this;
}

Dit si la cause est this retourne null sinon retourne la cause (qui peut également être null fur et à mesure.

L'histoire commence par ceci: private Throwable cause = this; qui est je crois la même chose dans toutes les versions> = 1.4. Cela initialise la cause à être cet objet!

L'intention est que les objets Throwable soient immuables mais cela fournit une méthode void initCause(Throwable cause) qui ne peut être appelée qu'une seule fois pour initialiser la cause ou la cause initialisée par un constructeur.

Comme l'explique la documentation, cela permet le chaînage des causes à des sous-classes ajoutées avant l'introduction de la cause qui ne l'incluent pas dans l'un de leurs constructeurs.

Ainsi, la classe veut en quelque sorte savoir si initCause a été appelée et lève IllegalStateException si elle a (mes commentaires):

 public synchronized Throwable getCause() {
     return (cause==this ? null : cause);
 }

La classe utilise cause==this pour indiquer la cause non définie. Il ne peut pas utiliser cause==null car null est une valeur valide. Donc, il utilise l'état anormal de cause==this parce que définir activement la cause à this est l'état illogique d'une exception auto-provoquée.

Cela fonctionne, bien sûr. Mais est-ce vraiment une bonne idée? Je dis non. Il confond les états «cause non définie» et «cause a été définie et est définie sur null ». Une conception moins contorsionnée introduit simplement un indicateur private boolean isCauseSet=false; et le définir si initCause est jamais appelé ou un constructeur qui le définit est appelé.

Le code alambiqué que nous voyons dans Throwable ne fait rien de plus que d'éviter un boolean . Enregistrer un seul champ boolean dans Throwable ne semble vraiment pas valoir la peine. Aucune application utile n'aura jamais autant d'objets Throwable en circulation pour que cela compte.


0 commentaires