J'ai un problème pour essayer les expressions Lambda de Java 8. Habituellement, cela fonctionne bien, mais maintenant j'ai des méthodes qui lancent IOException
des. Il est préférable de consulter le code suivant:
class Bank{
....
public Set<String> getActiveAccountNumbers() throws IOException {
Stream<Account> s = accounts.values().stream();
s = s.filter(a -> a.isActive());
Stream<String> ss = s.map(a -> a.getNumber());
return ss.collect(Collectors.toSet());
}
....
}
interface Account{
....
boolean isActive() throws IOException;
String getNumber() throws IOException;
....
}
Le problème est qu'il ne compile pas, car je dois attraper les exceptions possibles des méthodes isActive et getNumber. Mais même si j'utilise explicitement un try-catch-Block comme ci-dessous, il ne se compile toujours pas car je n'attrape pas l'exception. Donc, soit il y a un bogue dans JDK, soit je ne sais pas comment attraper ces exceptions.
class Bank{
....
//Doesn't compile either
public Set<String> getActiveAccountNumbers() throws IOException {
try{
Stream<Account> s = accounts.values().stream();
s = s.filter(a -> a.isActive());
Stream<String> ss = s.map(a -> a.getNumber());
return ss.collect(Collectors.toSet());
}catch(IOException ex){
}
}
....
}
Comment puis-je le faire fonctionner? Quelqu'un peut-il me suggérer la bonne solution?
java
exception-handling
lambda
java-8
Martin Weber
la source
la source
Réponses:
Vous devez intercepter l'exception avant qu'elle n'échappe au lambda:
Considérez le fait que le lambda n'est pas évalué à l'endroit où vous l'écrivez, mais à un endroit totalement indépendant, dans une classe JDK. Ce serait donc le moment où cette exception vérifiée serait lancée, et à cet endroit, elle n'est pas déclarée.
Vous pouvez le gérer en utilisant un wrapper de votre lambda qui traduit les exceptions vérifiées en exceptions non vérifiées:
Votre exemple serait écrit comme
Dans mes projets, je traite ce problème sans emballage; à la place, j'utilise une méthode qui désactive efficacement la vérification des exceptions par le compilateur. Inutile de dire que cela doit être traité avec précaution et que tout le monde sur le projet doit être conscient qu'une exception vérifiée peut apparaître là où elle n'est pas déclarée. Voici le code de plomberie:
et vous pouvez vous attendre à être
IOException
jeté au visage, même si vouscollect
ne le déclarez pas. Dans la plupart des cas, mais pas dans tous les cas réels, vous voudrez simplement renvoyer l'exception, de toute façon, et la traiter comme un échec générique. Dans tous ces cas, rien n'est perdu dans la clarté ou l'exactitude. Méfiez-vous simplement de ces autres cas, où vous voudriez réellement réagir à l'exception sur place. Le développeur ne sera pas informé par le compilateur qu'il y a unIOException
à attraper là-bas et le compilateur se plaindra en fait si vous essayez de l'attraper parce que nous l'avons trompé en lui faisant croire qu'aucune exception de ce genre ne peut être levée.la source
Vous pouvez également propager votre douleur statique avec des lambdas, de sorte que tout semble lisible:
propagate
ici reçoitjava.util.concurrent.Callable
en paramètre et convertit toute exception interceptée lors de l'appel enRuntimeException
. Il existe une méthode de conversion similaire Throwables # propagate (Throwable) dans Guava.Cette méthode semble être essentielle pour le chaînage de méthodes lambda, donc j'espère qu'un jour elle sera ajoutée à l'une des bibliothèques populaires ou que ce comportement de propagation le serait par défaut.
la source
Cette
UtilException
classe d'assistance vous permet d'utiliser toutes les exceptions vérifiées dans les flux Java, comme ceci:Remarque
Class::forName
jetteClassNotFoundException
, qui est vérifié . Le flux lui-même lève égalementClassNotFoundException
, et PAS une exception non cochée d'encapsulation.De nombreux autres exemples d'utilisation (après importation statique
UtilException
):Mais ne l'utilisez pas avant de comprendre les avantages, inconvénients et limitations suivants :
• Si le code d'appel 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.
• Si le code appelant 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 (si vous ne le faites pas, il dira: l'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 être toujours présent. Ici, la déclaration des lancers est une nuisance et toute solution pour la faire taire avec un passe-partout minimal est la bienvenue.
• Si vous détestez les exceptions vérifiées et pensez 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 simplement pas l'exception vérifiée au throws clause de la méthode qui contient le flux. L'exception vérifiée se comportera alors comme une exception non vérifiée.
• Si vous implémentez une interface stricte dans laquelle vous n'avez pas la possibilité d'ajouter une déclaration throws, mais que la levée d'une exception est tout à fait appropriée, alors encapsuler une exception juste pour obtenir le privilège de la lancer entraîne un stacktrace avec des exceptions fausses qui ne fournissent aucune information sur ce qui a réellement mal tourné. 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 qui contient le flux.
• Dans tous les cas, si vous décidez de NE PAS ajouter (ou oubliez d'ajouter) l'exception vérifiée à la clause throws de la méthode qui contient le flux, soyez conscient de ces 2 conséquences de lever des exceptions CHECKED:
1) Le code appelant ne pourra pas l'attraper par son nom (si vous essayez, le compilateur dira: l'exception n'est jamais levée dans le corps de l'instruction try correspondante). Il bouillonnera et sera probablement attrapé dans la boucle principale du programme par des "catch Exception" ou "catch Throwable", ce qui peut être ce que vous voulez de toute façon.
2) Cela viole le principe de moindre surprise: il ne suffira plus d'attraper RuntimeException pour pouvoir garantir la capture de toutes les exceptions possibles. Pour cette raison, je pense que cela ne devrait pas être fait dans le code-cadre, mais uniquement dans le code métier que vous contrôlez complètement.
En conclusion: je pense que les limites ici ne sont pas sérieuses et que la
UtilException
classe peut être utilisée sans crainte. Cependant, c'est à vous!la source
Vous pouvez potentiellement rouler votre propre
Stream
variante en enveloppant votre lambda pour lever une exception non vérifiée, puis en déroulant plus tard cette exception non vérifiée sur les opérations du terminal:Ensuite, vous pouvez l'utiliser exactement de la même manière que
Stream
:Cette solution nécessiterait un peu de passe-partout, donc je vous suggère de jeter un œil à la bibliothèque que j'ai déjà créée qui fait exactement ce que j'ai décrit ici pour toute la
Stream
classe (et plus encore!).la source
new StreamBridge<>(ts, IOException.class);
->new StreamBridge<>(s, IOException.class);
StreamAdapter
.Utilisez la méthode #propagate (). Exemple d'implémentation non-Guava du blog Java 8 par Sam Beran :
la source
Cela ne répond pas directement à la question (il y a beaucoup d'autres réponses qui le font) mais essaie d'éviter le problème en premier lieu:
D'après mon expérience, la nécessité de gérer les exceptions dans une
Stream
(ou une autre expression lambda) vient souvent du fait que les exceptions sont déclarées comme étant levées à partir de méthodes où elles ne devraient pas être levées. Cela vient souvent du mélange de logique métier avec entrée et sortie. VotreAccount
interface en est un parfait exemple:Au lieu de lancer un
IOException
sur chaque getter, considérez cette conception:La méthode
AccountReader.readAccount(…)
peut lire un compte à partir d'une base de données ou d'un fichier ou autre et lever une exception si elle échoue. Il construit unAccount
objet qui contient déjà toutes les valeurs, prêt à être utilisé. Comme les valeurs ont déjà été chargées parreadAccount(…)
, les getters ne lèveraient pas d'exception. Ainsi, vous pouvez les utiliser librement dans les lambdas sans avoir besoin d'encapsuler, de masquer ou de masquer les exceptions.Bien sûr, il n'est pas toujours possible de le faire comme je l'ai décrit, mais c'est souvent le cas et cela conduit à un code plus propre (IMHO):
throws IOException
pour rien d'autre que pour satisfaire le compilateurAccount
immuable et profiter de ses avantages (par exemple, la sécurité des fils)Account
dans les lambdas (par exemple dans aStream
)la source
Il peut être résolu par le code simple ci-dessous avec Stream and Try dans AbacusUtil :
Divulgation : Je suis le développeur de
AbacusUtil
.la source
En étendant la solution @marcg, vous pouvez normalement lancer et intercepter une exception vérifiée dans Streams; c'est-à-dire que le compilateur vous demandera de capturer / relancer comme vous étiez en dehors des flux !!
Ensuite, votre exemple serait écrit comme suit (en ajoutant des tests pour le montrer plus clairement):
la source
Pour ajouter correctement le code de gestion IOException (à RuntimeException), votre méthode ressemblera à ceci:
Le problème maintenant est que le
IOException
devra être capturé en tant queRuntimeException
et reconverti en unIOException
- et cela ajoutera encore plus de code à la méthode ci-dessus.Pourquoi l'utiliser
Stream
quand cela peut être fait comme ça - et la méthode lanceIOException
donc aucun code supplémentaire n'est nécessaire pour cela aussi:la source
En gardant ce problème à l'esprit, j'ai développé une petite bibliothèque pour traiter les exceptions vérifiées et les lambdas. Les adaptateurs personnalisés vous permettent d'intégrer les types fonctionnels existants:
https://github.com/TouK/ThrowingFunction/
la source
Votre exemple peut être écrit comme suit:
La classe Unthrow peut être prise ici https://github.com/SeregaLBN/StreamUnthrower
la source
Si cela ne vous dérange pas d'utiliser des bibliothèques tierces, la bibliothèque cyclops-react d'AOL ,closure :: Je suis un contributeur, a une classe ExceptionSoftener qui peut vous aider ici.
la source
Les interfaces fonctionnelles de Java ne déclarent aucune exception cochée ou décochée. Nous devons changer la signature des méthodes de:
À:
Ou manipulez-le avec le bloc try-catch:
Une autre option consiste à écrire un wrapper personnalisé ou à utiliser une bibliothèque telle que ThrowingFunction. Avec la bibliothèque, il suffit d'ajouter la dépendance à notre pom.xml:
Et utilisez les classes spécifiques telles que ThrowingFunction, ThrowingConsumer, ThrowingPredicate, ThrowingRunnable, ThrowingSupplier.
À la fin, le code ressemble à ceci:
la source
Je ne vois aucun moyen de gérer les exceptions vérifiées dans le flux (Java -8), la seule façon dont j'ai appliqué est de capturer les exceptions vérifiées dans le flux et de les relancer comme exception non vérifiée.
la source
Si vous souhaitez gérer l'exception dans le flux et continuer à traiter les autres, il existe un excellent article dans DZone de Brian Vermeer utilisant le concept de l'un ou l'autre. Cela montre une excellente façon de gérer cette situation. La seule chose qui manque est un exemple de code. Ceci est un exemple de mon exploration en utilisant les concepts de cet article.
la source