Contents

使用vavr处理lambda表达式中的异常

1. 简介

JDK 提供的*Functional Interfaces *没有为处理检查的异常做好适当的准备。如果您想了解有关该问题的更多信息,请查看本文

在本文中,我们将探讨使用函数式 Java 库Vavr 来解决此类问题的各种方法。

要获取有关 Vavr 以及如何设置它的更多信息,请查看这篇文章

2. 使用CheckedFunction

Vavr 提供了functional Interfaces ,这些接口具有抛出检查异常的函数。这些函数是CheckedFunction0CheckedFunction1等等,直到CheckedFunction8。函数名末尾的0 , 1, … 8表示函数的输入参数个数。

让我们看一个例子:

static Integer readFromFile(Integer integer) throws IOException {
    // logic to read from file which throws IOException
}

我们可以在 lambda 表达式中使用上述方法,而无需处理IOException

List<Integer> integers = Arrays.asList(3, 9, 7, 0, 10, 20);
CheckedFunction1<Integer, Integer> readFunction = i -> readFromFile(i);
integers.stream()
 .map(readFunction.unchecked());

如您所见,没有标准的try-catch或包装方法,我们仍然可以在 lambda 表达式中调用异常抛出方法。

我们在使用Stream API的这个特性时必须小心,因为异常会立即终止操作——放弃流的其余部分。

3. 使用辅助方法

API 类为上一节中的示例提供了一个快捷方法:

List<Integer> integers = Arrays.asList(3, 9, 7, 0, 10, 20);
integers.stream()
  .map(API.unchecked(i -> readFromFile(i)));

4. 使用提升

为了优雅地处理IOException ,我们可以在 lambda 表达式中引入标准的 try-catch块。但是,lambda 表达式的简洁性将会丢失。Vavr 的提升救了我们。

提升是函数式编程的一个概念。您可以将部分函数提升为返回Option作为结果的总函数。

部分函数是只为域的子集定义的函数,而不是为其整个域定义的总函数。如果使用超出其支持范围的输入调用部分函数,它通常会抛出异常。

让我们重写上一节中的示例:

List<Integer> integers = Arrays.asList(3, 9, 7, 0, 10, 20);
 
integers.stream()
  .map(CheckedFunction1.lift(i -> readFromFile(i)))
  .map(k -> k.getOrElse(-1));

请注意,提升函数的结果是Option,如果发生异常,结果将为Option.None 。在 Option.None 的情况下, *getOrElse()*方法采用备用值返回。

5. 使用Try

虽然上一节中的方法lift()解决了程序突然终止的问题,但它实际上吞下了异常。因此,我们方法的使用者不知道是什么导致了默认值。另一种方法是使用Try容器。

Try是一个特殊的容器,我们可以用它来封装一个可能引发异常的操作。在这种情况下,生成的Try对象代表一个*Failure *,它包装了异常。

让我们看一下使用Try的代码:

List<Integer> integers = Arrays.asList(3, 9, 7, 0, 10, 20);
integers.stream()
  .map(CheckedFunction1.liftTry(i -> readFromFile(i)))
  .flatMap(Value::toJavaStream)
  .forEach(i -> processValidValue(i));

要了解有关Try容器及其使用方法的更多信息,请查看这篇文章