使用vavr处理lambda表达式中的异常
1. 简介
JDK 提供的*Functional Interfaces *没有为处理检查的异常做好适当的准备。如果您想了解有关该问题的更多信息,请查看本文 。
在本文中,我们将探讨使用函数式 Java 库Vavr 来解决此类问题的各种方法。
要获取有关 Vavr 以及如何设置它的更多信息,请查看这篇文章 。
2. 使用CheckedFunction
Vavr 提供了functional Interfaces ,这些接口具有抛出检查异常的函数。这些函数是CheckedFunction0、CheckedFunction1等等,直到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容器及其使用方法的更多信息,请查看这篇文章 。