TL; DR
Prémisse
- Les exceptions d'exécution doivent être levées lorsque l'erreur est irrécupérable: lorsque l'erreur est dans le code et ne dépend pas de l'état externe (la récupération corrigerait donc le code).
- Les exceptions vérifiées doivent être levées lorsque le code est correct, mais que l'état externe n'est pas conforme aux attentes: absence de connectivité réseau, fichier introuvable ou corrompu, etc.
Conclusion
Nous pouvons réexaminer une exception vérifiée en tant qu'exception d'exécution si le code de propagation ou d'interface suppose que l'implémentation sous-jacente dépend d'un état externe, alors que ce n'est clairement pas le cas.
Cette section aborde le sujet du moment où l’une des exceptions doit être levée. Vous pouvez passer à la barre horizontale suivante si vous souhaitez simplement lire une explication plus détaillée de la conclusion.
Quand est-il approprié de lancer une exception d'exécution? Vous lève une exception d'exécution lorsqu'il est clair que le code est incorrect et que la récupération est appropriée en modifiant le code.
Par exemple, il est approprié de lancer une exception d'exécution pour les éléments suivants:
float nan = 1/0;
Cela lève une exception d'exécution division par zéro. Ceci est approprié car le code est défectueux.
Ou par exemple, voici une partie du HashMap
constructeur de 's:
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
// more irrelevant code...
}
Afin de fixer la capacité initiale ou le facteur de charge, il convient que vous modifiiez le code pour vous assurer que les valeurs correctes sont bien transmises. Cela ne dépend pas de la disponibilité d’un serveur distant, de l’état actuel du disque, un fichier ou un autre programme. Ce constructeur appelé avec des arguments non valides dépend de l'exactitude du code appelant, qu'il s'agisse d'un calcul erroné ayant conduit aux paramètres non valides ou d'un flux inapproprié manquant une erreur.
Quand est-il approprié de lancer une exception cochée? Vous lève une exception cochée lorsque le problème est récupérable sans changer le code. Ou, pour le dire autrement, vous lâchez une exception vérifiée lorsque l'erreur est liée à l'état alors que le code est correct.
Maintenant, le mot "récupérer" peut être délicat ici. Cela peut signifier que vous trouvez un autre moyen d'atteindre l'objectif: par exemple, si le serveur ne répond pas, vous devriez essayer le serveur suivant. Si ce type de récupération est possible pour votre cas, c'est bien, mais ce n'est pas la seule chose que signifie récupération - la récupération peut simplement afficher une boîte de dialogue d'erreur indiquant à l'utilisateur ce qui s'est passé, ou bien s'il s'agit d'une application serveur, alors envoyer un courrier électronique à l'administrateur, ou même simplement consigner l'erreur de manière appropriée et concise.
Prenons l'exemple cité dans la réponse de mrmuggles:
public void dataAccessCode(){
try{
..some code that throws SQLException
}catch(SQLException ex){
throw new RuntimeException(ex);
}
}
Ce n'est pas la bonne façon de gérer l'exception vérifiée. La simple incapacité à gérer l'exception dans le champ d'application de cette méthode ne signifie pas que l'application doit être plantée. Au lieu de cela, il convient de le propager à une portée plus élevée comme ceci:
public Data dataAccessCode() throws SQLException {
// some code that communicates with the database
}
Ce qui permet la possibilité de récupération par l'appelant:
public void loadDataAndShowUi() {
try {
Data data = dataAccessCode();
showUiForData(data);
} catch(SQLException e) {
// Recover by showing an error alert dialog
showCantLoadDataErrorDialog();
}
}
Les exceptions cochées sont un outil d'analyse statique, elles indiquent clairement à un programmeur ce qui pourrait mal tourner lors d'un appel donné sans lui demander d'apprendre la mise en oeuvre ou de passer par un processus d'essais et d'erreurs. Cela permet de s'assurer facilement qu'aucune partie du flux d'erreur ne sera ignorée. Le fait de renvoyer une exception vérifiée en tant qu'exception d'exécution va à l'encontre de cette fonctionnalité d'analyse statique qui économise du travail.
Il est également intéressant de mentionner que la couche appelante a un meilleur contexte du plus grand schéma des choses, comme cela a été démontré ci-dessus. Il pourrait y avoir plusieurs causes à dataAccessCode
l'appel, la raison spécifique de l'appel n'étant visible que par l'appelant - il est donc en mesure de prendre une meilleure décision lors de la récupération correcte en cas d'échec.
Maintenant que cette distinction est claire, nous pouvons en déduire le moment opportun pour réexaminer une exception vérifiée en tant qu’exception d’exécution.
Compte tenu de ce qui précède, à quel moment convient-il de rétablir une exception vérifiée en tant qu'exception RuntimeException? Lorsque le code que vous utilisez suppose une dépendance à un état externe, mais vous pouvez clairement affirmer qu'il ne dépend pas d'un état externe.
Considérer ce qui suit:
StringReader sr = new StringReader("{\"test\":\"test\"}");
try {
doesSomethingWithReader(sr); // calls #read, so propagates IOException
} catch (IOException e) {
throw new IllegalStateException(e);
}
Dans cet exemple, le code se propage IOException
car l'API de Reader
est conçue pour accéder à un état externe. Cependant, nous savons que la StringReader
mise en œuvre n'accède pas à un état externe. À ce niveau, où nous pouvons certainement affirmer que les parties impliquées dans l’appel n’ont pas accès à IO ou à tout autre état externe, nous pouvons sans risque rediffuser l’exception comme une exception d’exécution, sans surprendre des collègues qui ne sont pas au courant de notre implémentation (et sont éventuellement en supposant que le code d'accès IO jettera un IOException
).
La raison pour garder strictement vérifiées les exceptions dépendantes de l'état externe est qu'elles ne sont pas déterministes (contrairement aux exceptions dépendantes de la logique, qui seront reproduites de manière prévisible pour une version du code). Par exemple, si vous essayez de diviser par 0, vous obtiendrez toujours une exception. Si vous ne divisez pas par 0, vous ne produirez jamais d'exception et vous ne devrez pas gérer ce cas d'exception, car cela n'arrivera jamais. Dans le cas de l'accès à un fichier, toutefois, réussir une fois ne signifie pas que vous réussirez la prochaine fois: l'utilisateur peut avoir modifié les autorisations, un autre processus peut l'avoir supprimé ou modifié. Donc, vous devez toujours gérer ce cas exceptionnel, ou vous avez probablement un bogue.