假设我们尝试向java 8流应用一个可能抛出已检查异常的lambda:
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
这不会编译。
一种解决方法是在RuntimeException
中嵌套已检查的异常,但这会使以后的异常处理变得复杂,而且只是丑陋:
stream.forEach(s -> {
try {
writer.append(s);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
替代解决方法可能是将limited功能forEach
转换为plain old foreach loop,这对检查异常更友好。
但天真的方法失败了:
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);
}
更新
回答这个问题的一个技巧在Why does Stream<T> not implement Iterable<T>?以side answer为主,并没有真正回答这个问题本身。 我认为这不足以将这个问题视为该问题的重复,因为他们会提出不同的问题。
答案 0 :(得分:28)
By definition foreach循环需要传入Iterable。
可以使用匿名类来实现:
for (String s : new Iterable<String>() {
@Override
public Iterator<String> iterator() {
return stream.iterator();
}
}) {
writer.append(s);
}
这可以通过lambda进行简化,因为Iterable
是functional interface:
for (String s : (Iterable<String>) () -> stream.iterator()) {
writer.append(s);
}
这可以转换为method reference:
for (String s : (Iterable<String>) stream::iterator) {
writer.append(s);
}
使用中间变量或方法参数可以避免显式强制转换:
Iterable<String> iterable = stream::iterator;
for (String s : iterable) {
writer.append(s);
}
maven中心还有StreamEx库,它具有可迭代流和开箱即用的其他特权。
以下是一些最受欢迎的问题和方法,它们提供了lambda和流中已检查异常处理的变通方法:
Java 8 Lambda function that throws exception?
Java 8: Lambda-Streams, Filter by Method with Exception
Java 8: Mandatory checked exceptions handling in lambda expressions. Why mandatory, not optional?
Kotlin;)
答案 1 :(得分:1)
Stream不实现Iterable,也不是数组,因此不适合在增强的for循环中使用。它没有实现Iterable的原因是因为它是一个单一用途的数据结构。每次调用Iterable.iterator时,都应返回一个覆盖所有元素的新迭代器。 Stream上的迭代器方法只反映了Stream的当前状态。它实际上是对Stream的不同看法。
您可以通过包装流以在增强的for循环中使用来创建实现Iterable的类。但这是一个值得怀疑的想法,因为你无法真正实现Iterable(如上所述)。
答案 2 :(得分:1)
I wrote an extension到Stream API,允许抛出已检查的异常。
Stream<String> stream = Stream.of("1", "2", "3");
Writer writer = new FileWriter("example.txt");
ThrowingStream.of(stream, IOException.class).forEach(writer::append);
答案 3 :(得分:1)
你也可以这样做:
Path inputPath = Paths.get("...");
Stream<Path> stream = Files.list(inputPath);
for (Iterator<Path> iterator = stream.iterator(); iterator.hasNext(); )
{
Path path = iterator.next();
// ...
}