lambda表达式有利于我们写出非常简洁的代码,但是Java8中lambda表达式无法抛出受检异常,这样会使我们在lambda表达式中使用抛出Checked Exception的方法时,会出现一些问题,接下来我们就来探讨一下这个问题。
一 问题
首先看一下出现问题的代码:
// 获取文件里的文本内容
public Stream<String> readFiles(String path) throws IOException {
return Files.lines(Paths.get(getClass().getClassLoader().getResource(path).getPath()));
}
public void test3() {
Stream.of("a.txt")
.flatMap(this::readFiles) // 这里会出现编译异常
.forEach(System.out::println);
}
到这里,我们就会发现一个编译问题:
Error:(64, 26) java: 方法引用中抛出的类型java.io.IOException不兼容
这是由于flatMap()
支持的函数式接口
@FunctionalInterface
public interface Function<T, R> {
/**
* 并不支持抛出异常(没有申明throw)
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
}
二 问题
处理这种受检异常时,我们可以先捕获,然后包装并抛出一个非受检异常。
方法一
public void test1() {
Stream.of("a.txt")
.flatMap(path -> {
try {
return readFiles(path);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException();
}
})
.forEach(System.out::println);
}
这样可以解决问题,但是会损失lambda的简洁性。
方法二
出现编译时异常的主要问题还是由于Function的apply方法不支持抛出异常,我们可以定义自己的函数式接口:
@FunctionalInterface
public interface ThrowingFunction<T,R,E extends Throwable> {
R apply(T arg) throws E;
}
然后在使用一个包装器
static <T, R, E extends Exception> Function<T, R> unchecked(ThrowingFunction<T, R, E> f) {
return t -> {
try {
return apply(t);
} catch (final Throwable e) {
throw new WrappedException(e);
}
};
}
然后就可以这样使用了:
@Test
public void test5() {
Stream.of("a.txt")
.flatMap(unchecked(this::readFiles))
.forEach(System.out::println);
}
这样的话就保证了代码的间接性,但是我们又不得不定义对应的函数式接口和对应的包装器。对于这个问题,我们可以利用一个开源库ThrowingFunction
来解决。方法如下:
1 添加依赖
<dependency>
<groupId>pl.touk</groupId>
<artifactId>throwing-function</artifactId>
<version>1.3</version>
</dependency>
2 代码
@Test
public void test4() {
Stream.of("a.txt")
.flatMap(ThrowingFunction.unchecked(this::readFiles))
.forEach(System.out::println);
}
到此为止,我们可以ThrowingFunction
很好的解决这种检测异常问题!