Supposons que nous essayons d'appliquer à Java 8 stream un lambda qui pourrait lever l'exception vérifiée:
Stream<String> stream = Stream.of("1", "2", "3");
Writer writer = new FileWriter("example.txt");
stream.forEach(s -> writer.append(s)); // Unhandled exception: Java.io.IOException
Cela ne se compilera pas.
Une solution de contournement consiste à imbriquer l'exception vérifiée dans RuntimeException
mais cela complique la gestion des exceptions ultérieures et c'est juste moche:
stream.forEach(s -> {
try {
writer.append(s);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
Une solution alternative pourrait être de convertir limité fonctionnel forEach
en plain old foreachboucle qui est plus convivial pour les exceptions vérifiées.
Mais les approches naïves échouent:
for (String s : stream) { // for-each not applicable to expression type 'Java.util.stream.Stream<Java.lang.String>'
writer.append(s);
}
for (String s : stream.iterator()) { // foreach not applicable to type 'Java.util.Iterator<Java.lang.String>'
writer.append(s);
}
Mise à jour
Une astuce qui répond à cette question a été publiée précédemment sur Pourquoi Stream <T> n'implémente-t-il pas Iterable <T>? as réponse latérale qui ne répond pas vraiment à cette question elle-même. Je pense que cela ne suffit pas pour qualifier cette question de duplicata de celle-ci car ils demandent des choses différentes.
Par définition pour chaque boucle, un Iterable doit être transmis.
Cela peut être réalisé avec une classe anonyme:
for (String s : new Iterable<String>() {
@Override
public Iterator<String> iterator() {
return stream.iterator();
}
}) {
writer.append(s);
}
Cela peut être simplifié avec lambda car Iterable
est une interface fonctionnelle :
for (String s : (Iterable<String>) () -> stream.iterator()) {
writer.append(s);
}
Cela peut être converti en référence de méthode :
for (String s : (Iterable<String>) stream::iterator) {
writer.append(s);
}
La conversion explicite peut être évitée avec une variable intermédiaire ou un paramètre de méthode:
Iterable<String> iterable = stream::iterator;
for (String s : iterable) {
writer.append(s);
}
Il y a aussi StreamEx bibliothèque dans maven central qui propose des flux itérables et d'autres avantages hors de la boîte.
Voici quelques questions et approches les plus populaires qui fournissent des solutions de contournement sur la gestion des exceptions vérifiées dans les lambdas et les flux:
Fonction Lambda Java 8 qui lève une exception?
Java 8: Lambda-Streams, filtrer par méthode avec exception
Kotlin ;)
I a écrit une extension à l'API Stream qui permet de lever les exceptions vérifiées.
Stream<String> stream = Stream.of("1", "2", "3");
Writer writer = new FileWriter("example.txt");
ThrowingStream.of(stream, IOException.class).forEach(writer::append);
Vous pouvez également le faire comme ceci:
Path inputPath = Paths.get("...");
Stream<Path> stream = Files.list(inputPath);
for (Iterator<Path> iterator = stream.iterator(); iterator.hasNext(); )
{
Path path = iterator.next();
// ...
}
Stream n'implémente pas Iterable, ni un tableau, il n'est donc pas éligible pour une utilisation dans une boucle for améliorée. La raison pour laquelle il n'implémente pas Iterable est qu'il s'agit d'une structure de données à usage unique. Chaque fois que Iterable.iterator est appelé, un nouvel Iterator doit être retourné qui couvre tous les éléments. La méthode itérateur sur Stream reflète simplement l'état actuel du Stream. Il s'agit en fait d'une vue différente du Stream.
Vous pouvez créer une classe qui implémente Iterable en encapsulant un flux à utiliser dans la boucle for améliorée. Mais c'est une idée discutable car vous ne pouvez pas vraiment implémenter Iterable (comme décrit ci-dessus).