Lambda 中的异常

JAVA 流不能很好地处理已检查的异常 。在本文中,深入探讨如何管理此类问题 。 
Java 引入了检查异常的概念 。与早期的方法相比,强制开发人员管理异常的想法是革命性的 。
如今,Java 仍然是唯一一种提供检查异常的广泛使用的语言 。例如,Kotlin 中的每个异常都是未经检查的 。
即使在 Java 中,新特性也与受检异常不一致:Java 内置函数式接口的签名不使用异常 。当将遗留代码集成到 lambda 中时,会导致代码繁琐 。这在 Streams 中很明显 。
在这篇文章中,我想深入探讨如何处理此类问题 。
 
代码中的问题 
下面是一个示例代码来说明这个问题:
Stream.of("java.lang.String", "ch.frankel.blog.Dummy", "java.util.ArrayList")
.map(it -> new ForNamer().Apply(it)) // 1
.forEach(System.out::println);
 

  1. 不编译:需要捕获已检查ClassNotFoundException
 
我们必须添加一个 try/catch 块来修复编译问题 。
Stream.of("java.lang.String", "ch.frankel.blog.Dummy", "java.util.ArrayList")
.map(it -> {
try {
return Class.forName(it);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
})
.forEach(System.out::println);
添加块破坏了易于阅读的管道的目的 。
 
将 Try/Catch 块封装到一个类中 
为了恢复可读性,我们需要重构代码以引入一个新类 。IntelliJ IDEA 甚至提出了一条记录:
var forNamer = new ForNamer(); // 1
Stream.of("java.lang.String", "ch.frankel.blog.Dummy", "java.util.ArrayList")
.map(forNamer::apply) // 2
.forEach(System.out::println);
record ForNamer() implements Function> {
@Override
public Class apply(String string) {
try {
return Class.forName(string);
} catch (ClassNotFoundException e) {
return null;
}
}
}
 
  1. 创建单个记录对象 。
  2. 重复使用它 。
尝试龙目岛 
Project Lombok 是一个编译时注释处理器,可生成额外的字节码 。一个人使用正确的注释并获得结果,而无需编写样板代码 。
 
Project Lombok 是一个 Java 库,可自动插入您的编辑器和构建工具,为您的 Java 增添趣味 。永远不要再编写另一个 getter 或 equals 方法,使用一个注释,您的类就有一个功能齐全的构建器、自动化您的日志记录变量等等 。
-龙目岛计划
 
Lombok 提供了@SneakyThrow注解:它允许人们抛出已检查的异常,而无需在自己的方法签名中声明它们 。然而,它目前不适用于现有的 API 。
如果您是 Lombok 用户,请注意有一个已打开的 GitHub 问题,其状态为停放 。
 
Commons Lang 救援 
Apache Commons Lang是一个古老的项目 。它在当时很普遍,因为它提供的实用程序可能是 Java API 的一部分,但不是 。这是一个比在每个项目中重新发明你的DateUtils和更好的选择 。StringUtils在研究这篇文章时,我发现它仍然定期使用很棒的 API 进行维护 。其中之一是FailableAPI 。
API 由两部分组成:
 
  1. 一个包装器Stream
  2. 签名接受异常的管道方法
 
这是一个小摘录:
Lambda 中的异常

文章插图
代码终于变成了我们一开始就期待的样子:
Stream stream = Stream.of("java.lang.String", "ch.frankel.blog.Dummy", "java.util.ArrayList");
Failable.stream(stream)
.map(Class::forName) // 1
.forEach(System.out::println);
 
修复编译时错误是不够的 
【Lambda 中的异常】前面的代码在运行时ClassNotFoundException抛出一个Wrapped in an。我们满足了编译器,但我们无法指定预期的行为:UndeclaredThrowableException
 
  1. 抛出第一个异常
  2. 丢弃异常
  3. 聚合类和异常,以便我们可以在管道的最后阶段对它们采取行动
  4. 别的东西
 
为了实现这一点,我们可以利用 Vavr 的力量 。Vavr 是一个将函数式编程的强大功能带入 Java 语言的库:
 
Vavr 核心是 Java 的函数库 。它有助于减少代码量并增加健壮性 。函数式编程的第一步是开始思考不可变的值 。Vavr 提供了不可变的集合以及对这些值进行操作所需的函数和控制结构 。结果很漂亮,而且很有效 。


推荐阅读