Comment gérer les exceptions vérifiées qui ne peuvent jamais être levées

35

Exemple:

foobar = new InputStreamReader(p.getInputStream(), "ISO-8859-1");

Comme l'encodage est codé en dur et correct, le constructeur ne lève jamais l'exception UnsupportedEncodingException déclarée dans la spécification (à moins que l'implémentation java soit interrompue, auquel cas je suis perdu de toute façon). Quoi qu'il en soit, Java m'oblige de toute façon à traiter cette exception.

Actuellement, ça ressemble à ça

try {
    foobar = new InputStreamReader(p.getInputStream(), "ISO-8859-1");
}
catch(UnsupportedEncodingException e) { /* won't ever happen */ }

Des idées pour l'améliorer?

utilisateur281377
la source
4
Pour un véritable défi, écrivez un test unitaire pour vous assurer que cette capture fonctionne réellement.
Jay Elston
1
`jeter nouvelle ImpossibleException (" Redémarrez l'univers. Les choses sont gâchées ", e);
Chris Cudmore
1
"
Thorbjørn Ravn Andersen: il se peut qu’après le changement de programme, mais au moins dans son état actuel, aucun ensemble de données d’entrée ne puisse déclencher l’exception.
user281377
Dans votre exemple spécifique ci-dessus, une exception est générée car la méthode ne sait pas si elle sera appelée avec un codage pris en charge ou non. Un moyen de contourner ce problème serait si un autre constructeur supposait qu'un jeu de caractères standard était donné, sans qu'il soit nécessaire de déclarer qu'une exception serait levée.
Jordanie

Réponses:

27

Mon habitude est, par sécurité, de mettre un point assertdans le bloc. Quelqu'un pourrait changer le contenu du trybloc plus tard, et vous voulez savoir si le code échoue, n'est-ce pas?

Péter Török
la source
Bonne idée; un simple assert false;n'ajoute pas trop de fouillis et indique clairement que je suppose que le bloc de capture ne sera jamais entré.
user281377
5
@ammoQ, ou vous pouvez même ajouter un message à faire votre intention tout à fait claire: assert false : "should never happen".
Péter Török
13
Mieux encore, "Cela ne devrait jamais arriver, car Java nécessite la prise en charge du jeu de caractères ISO-8859-1."
Dan04
3
assertimplique que les assertions sont activées. Je jette un UnexpectedException(qui a aussi l'avantage de me laisser une trace de pile ...).
Couper
35

Si on me donnait un centime à chaque fois que je voyais un journal / une erreur, "cela ne devrait jamais arriver", j'aurais ... eh bien, deux centimes. Mais reste...

Les blocs d'arrêt vides font vibrer mes sens et les plus bons outils d'analyse de code se plaignent. J'éviterais à tout prix de les laisser vides. Bien sûr, vous savez maintenant que l'erreur ne peut jamais se produire, mais dans un an, quelqu'un effectuera une recherche-remplacement global de "ISO-8859-1" et vous risquez d'avoir un bogue extrêmement difficile à trouver.

La assert falsesuggestion est bonne, mais comme les assertions peuvent être désactivées au moment de l'exécution, elles ne constituent aucune garantie. Je voudrais utiliser un RuntimeExceptionlieu. Ceux-ci n'auront pas à être attrapés en appelant des classes et s'ils se produisent, vous aurez une trace de pile pour donner des informations complètes.

Fredrik
la source
28

Je l'ai toujours fait comme ça:

try {
    foobar = new InputStreamReader(p.getInputStream(), "ISO-8859-1");
} catch(UnsupportedEncodingException e) {
    throw new AssertionError(e);
}

Peut-être un peu prolixe (Java est ...), mais au moins vous obtiendrez une erreur d'assertion lorsque l'impossible se produira.

Si l'implémentation Java est interrompue, vous souhaiterez obtenir le meilleur message d'erreur possible, le plus rapidement possible, au lieu de simplement ignorer l'impossible. Et même si l'implémentation Java n'est pas rompue, quelqu'un aurait pu changer votre code en "UTF8"(oups - devrait-il en être ainsi "UTF-8"?).

Cela aurait dû être une exception d'exécution en premier lieu. JDK est plein de ce genre de mauvais choix.

Joonas Pulakka
la source
4
La véritable mauvaise décision (ou omission si vous le souhaitez) est qu’il n’existe aucune instance de jeu de caractères prédéfinie pour les 6 jeux de caractères requis par Java. foobar = new InputStreamReader(p.getInputStream(), Charset.ISO_8859_1);- cela ne serait-il pas plus agréable et d'éviter toute erreur une fois pour toutes?
user281377
3
La bibliothèque de goyave a des tonnes de ces choses. Rend la vie plus facile.
3
Java 1.7+ a défini des constantes de jeu de caractères: docs.oracle.com/javase/7/docs/api/java/nio/charset/… . Utilisez-les comme suit: StandardCharsets.UTF_8.displayName ()
Michael
4

Si vous êtes le seul développeur à avoir accès à ce code, je dirais que c'est correct, mais si vous ne l'êtes pas, je le traiterais comme une possibilité réelle ou du moins changerais le commentaire "ne se produira jamais". quelque chose de plus utile.

Thanos Papathanasiou
la source
4

La partie de ces exceptions qui m'agace le plus est que cela nuit à la couverture de mon code.

Lorsque la couverture devient compulsive, je vais essayer de comprendre que "ne peut jamais arriver" (... ou seulement si j'utilise une JVM mutante qui avait oublié d'inclure "US-ASCII") dans une classe et une méthode qui encapsule cette tentative / intercepte et remplace l’exception vérifiée de l’une des manières mentionnées ici (généralement, en lançant une exception non contrôlée avec un message discret).

Ensuite, ma couverture de code prend un coup dans la classe utilitaire, mais pas dans toutes les références à cette opération dispersées autour de mon code.

Parfois, je prends le temps de faire comme dans une classe où la sémantique est cohérente. Mais comme il est assez évident pour mes coéquipiers de savoir ce qui se passe, je tiens généralement à le garder aussi simple que possible et à ne pas trop m'inquiéter du meilleur design possible.

Cependant, comme mentionné dans un commentaire, Guava et d'autres bibliothèques ont des moyens d'atténuer cette douleur - mais c'est fondamentalement la même stratégie. Déplacez le désagrément en dehors de la scène afin que votre code principal ne soit pas touché par la couverture.

Rob
la source