J'apprécie beaucoup les nouvelles fonctionnalités de Java 8 concernant les interfaces de méthode lambdas et par défaut. Pourtant, je me lasse toujours des exceptions vérifiées. Par exemple, si je veux juste lister tous les champs visibles d'un objet, j'aimerais simplement écrire ceci:
Arrays.asList(p.getClass().getFields()).forEach(
f -> System.out.println(f.get(p))
);
Cependant, comme la get
méthode peut générer une exception vérifiée, ce qui ne correspond pas au Consumer
contrat d'interface, je dois alors intercepter cette exception et écrire le code suivant:
Arrays.asList(p.getClass().getFields()).forEach(
f -> {
try {
System.out.println(f.get(p));
} catch (IllegalArgumentException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
);
Cependant, dans la plupart des cas, je souhaite simplement que l'exception soit émise en tant que RuntimeException
et laisse le programme gérer, ou non, l'exception sans erreurs de compilation.
Je voudrais donc connaître votre opinion sur la solution de contournement controversée que je propose pour la gêne des exceptions vérifiées. À cette fin, j'ai créé une interface auxiliaire ConsumerCheckException<T>
et une fonction utilitaire rethrow
( mise à jour conformément à la suggestion du commentaire de Doval ) comme suit:
@FunctionalInterface
public interface ConsumerCheckException<T>{
void accept(T elem) throws Exception;
}
public class Wrappers {
public static <T> Consumer<T> rethrow(ConsumerCheckException<T> c) {
return elem -> {
try {
c.accept(elem);
} catch (Exception ex) {
/**
* within sneakyThrow() we cast to the parameterized type T.
* In this case that type is RuntimeException.
* At runtime, however, the generic types have been erased, so
* that there is no T type anymore to cast to, so the cast
* disappears.
*/
Wrappers.<RuntimeException>sneakyThrow(ex);
}
};
}
/**
* Reinier Zwitserloot who, as far as I know, had the first mention of this
* technique in 2009 on the java posse mailing list.
* http://www.mail-archive.com/[email protected]/msg05984.html
*/
public static <T extends Throwable> T sneakyThrow(Throwable t) {
throw (T) t;
}
}
Et maintenant je peux juste écrire:
Arrays.asList(p.getClass().getFields()).forEach(
rethrow(f -> System.out.println(f.get(p)))
);
Je ne suis pas sûr que ce soit l'idiome le plus approprié pour contourner les exceptions vérifiées, mais comme je l'ai expliqué, j'aimerais disposer d'un moyen plus pratique de réaliser mon premier exemple sans traiter des exceptions vérifiées. C'est le moyen le plus simple que j'ai trouvé. pour le faire.
la source
sneakyThrow
dansrethrow
pour lancer l'original, exception vérifiée au lieu de l'envelopper dans un fichierRuntimeException
. Vous pouvez également utiliser l'@SneakyThrows
annotation de Project Lombok qui fait la même chose.Consumer
s inforEach
peut être exécuté de manière parallèle lors de l’utilisation deStream
s parallèles . Un jetable généré chez le consommateur se propage ensuite vers le fil appelant, ce qui 1) n'arrête pas les autres consommateurs exécutant simultanément, ce qui peut ou non être approprié, et 2) si plus d'un consommateur jette quelque chose, uniquement l'un des objets jetables sera vu par le fil d'appel.Réponses:
Avantages, inconvénients et limites de votre technique:
Si le code appelant doit gérer l'exception vérifiée, vous DEVEZ l'ajouter à la clause throws de la méthode qui contient le flux. Le compilateur ne vous obligera plus à l'ajouter, il est donc plus facile de l'oublier. Par exemple:
Si le code d'appel gère déjà l'exception vérifiée, le compilateur vous rappellera d'ajouter la clause throws à la déclaration de méthode qui contient le flux (sinon, il dira: une exception n'est jamais levée dans le corps de l'instruction try correspondante) .
Dans tous les cas, vous ne pourrez pas entourer le flux lui-même pour attraper l'exception vérifiée À L'INTÉRIEUR de la méthode qui contient le flux (si vous essayez, le compilateur dira: L'exception n'est jamais levée dans le corps de l'instruction try correspondante).
Si vous appelez une méthode qui ne peut littéralement jamais lever l'exception qu'elle déclare, vous ne devez pas inclure la clause throws. Par exemple: new String (byteArr, "UTF-8") lève une exception UnsupportedEncodingException, mais UTF-8 est garanti par la spécification Java pour qu'il soit toujours présent. Ici, la déclaration des lancers est une nuisance et toute solution pour la réduire au silence avec un passe-partout minimal est la bienvenue.
Si vous détestez les exceptions vérifiées et estimez qu'elles ne devraient jamais être ajoutées au langage Java pour commencer (un nombre croissant de personnes pensent de cette façon, et je ne suis pas l'un d'entre eux), alors n'ajoutez pas l'exception vérifiée aux lancers. clause de la méthode qui contient le flux. L'exception cochée se comportera alors comme une exception non vérifiée.
Si vous implémentez une interface stricte pour laquelle vous n'avez pas l'option d'ajouter une déclaration de projection et que le lancement d'une exception est tout à fait approprié, encapsuler une exception uniquement pour avoir le privilège de le lancer résulte en un stacktrace avec des exceptions fausses qui ne donne aucune information sur ce qui s'est réellement passé. Un bon exemple est Runnable.run (), qui ne lève aucune exception vérifiée. Dans ce cas, vous pouvez décider de ne pas ajouter l'exception vérifiée à la clause throws de la méthode contenant le flux.
Dans tous les cas, si vous décidez de NE PAS ajouter (ou ne pas oublier d’ajouter) l’exception vérifiée à la clause throws de la méthode qui contient le flux, tenez compte de ces 2 conséquences de la levée des exceptions CHECKED:
Le code appelant ne pourra pas l'attraper par son nom (si vous essayez, le compilateur dira: une exception n'est jamais levée dans le corps de l'instruction try correspondante). Il fera des bulles et sera probablement attrapé dans la boucle principale du programme par un "catch Exception" ou un "catch Throwable", ce qui peut être ce que vous voulez de toute façon.
Cela viole le principe de la moindre surprise: il ne suffira plus d'attraper RuntimeException pour pouvoir garantir la capture de toutes les exceptions possibles. Pour cette raison, j'estime que cela ne devrait pas être fait dans le code cadre, mais uniquement dans le code métier que vous contrôlez complètement.
Références:
REMARQUE: Si vous décidez d'utiliser cette technique, vous pouvez copier la
LambdaExceptionUtil
classe d'assistance de StackOverflow: https://stackoverflow.com/questions/27644361/how-can-i-throw-checked-exceptions-from-inside-java-8. -streams . Il vous donne l'implémentation complète (Fonction, Consommateur, Fournisseur ...), avec des exemples.la source
Dans cet exemple, cela peut-il vraiment échouer? Ne crois pas, mais peut-être que ton cas est spécial. Si c'est vraiment "ne peut pas échouer", et que c'est juste un truc ennuyeux pour le compilateur, j'aime bien emballer l'exception et lancer un
Error
avec un commentaire " ça ne peut pas arriver". Rend les choses très claires pour la maintenance. Sinon, ils se demanderont "comment cela peut-il arriver" et "qui diable gère cela?"Ceci dans une pratique controversée donc YMMV. Je vais probablement avoir des votes négatifs.
la source
clone
un type scelléFoo
dont on sait qu’il le supporte et qu’il jetteCloneNotSupportedException
. Comment cela pourrait-il se produire à moins que le code ne soit lié à un autre type inattenduFoo
? Et si cela se produit, peut-on faire confiance à quelque chose?An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch.
(cité dans le Javadoc deError
) C’est exactement ce genre de situation, donc loin d’être inadéquate, lancer unError
ici est l’option la plus appropriée.une autre version de ce code où exception vérifiée est juste retardée:
la magie est que si vous appelez:
alors votre code sera obligé d'attraper l'exception
la source
SneakyThrow est cool! Je ressens totalement ta douleur.
Si vous devez rester en Java ...
Paguro a des interfaces fonctionnelles qui encapsulent les exceptions vérifiées afin que vous n'ayez plus à y penser. Il inclut des collections immuables et des transformations fonctionnelles similaires aux transducteurs Clojure (ou Java 8 Streams) qui prennent les interfaces fonctionnelles et encapsulent les exceptions vérifiées.
Il y a aussi VAVr et les collections Eclipse à essayer.
Sinon, utilisez Kotlin
Kotlin est compatible bi -directionnel avec Java, ne contient pas d'exceptions vérifiées, pas de primitives (enfin, le programmeur n'a pas à y penser), un meilleur système de types, suppose l'immutabilité, etc. Même si je gère la bibliothèque Paguro ci-dessus, je m convertir tout le code Java que je peux en Kotlin. Tout ce que je faisais auparavant en Java, je préfère maintenant le faire à Kotlin.
la source
Les exceptions cochées sont très utiles et leur traitement dépend du type de produit dont vous définissez le code:
Habituellement, une bibliothèque ne doit pas gérer les exceptions vérifiées, mais déclarer qu'elle est levée par une API publique. Le cas rare où ils pourraient être intégrés à RuntimeExcepton est, par exemple, la création d'un document xml privé à l'intérieur du code de bibliothèque, son analyse, la création d'un fichier temporaire, puis sa lecture.
Pour les applications de bureau, vous devez commencer le développement du produit en créant votre propre infrastructure de traitement des exceptions. En utilisant votre propre framework, vous allez généralement encapsuler une exception vérifiée dans deux à quatre wrappers (vos sous-classes personnalisées de RuntimeExcepion) et les gérer avec un seul gestionnaire d'exception.
Pour les serveurs, généralement votre code s’exécutera dans une structure donnée. Si vous n'en utilisez aucun (serveur nu), créez votre propre cadre similaire (basé sur Runtimes) décrit ci-dessus.
Pour les exercices académiques, vous pouvez toujours les envelopper directement dans RuntimeExcepton. Quelqu'un peut aussi créer un petit cadre.
la source
Vous voudrez peut-être jeter un coup d'œil à ce projet ; Je l'ai créé spécifiquement à cause du problème des exceptions vérifiées et des interfaces fonctionnelles.
Avec cela, vous pouvez faire cela au lieu d'écrire votre code personnalisé:
Puisque toutes les interfaces Throwing * étendent leurs homologues non Throwing, vous pouvez les utiliser directement dans le flux:
Ou vous pouvez simplement:
Et d'autres choses.
la source
fields.forEach((ThrowingConsumer<Field>) f -> out.println(f.get(o)))