Java8 Lambda Handling Exceptions


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很好的解决这种检测异常问题!


文章作者: shiv
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 shiv !
评论
  目录