Java引入了已检异常的概念。与早期的方法相比,强制开发人员管理异常的想法是革命性的。
创新互联专注于网站建设|网站建设维护|优化|托管以及网络推广,积累了大量的网站设计与制作经验,为许多企业提供了网站定制设计服务,案例作品覆盖成都玻璃隔断等行业。能根据企业所处的行业与销售的产品,结合品牌形象的塑造,量身开发品质网站。现在,Java仍然是唯一广泛使用的提供已检异常的语言。例如,Kotlin中的每个异常都是未检查的。
即使在Java中,新特性也和已检异常不一致:Java内置函数式接口的签名不使用异常。在lambda中集成遗留代码时,代码会变得很麻烦。这在溪流中很明显。
在这篇文章中,我想深入探讨如何管理这样的问题。
代码中的问题下面的示例代码说明了这个问题:
不能编译:需要捕获已检查的ClassNotFoundException我们必须添加一个try/catch块来修复编译问题。
Stream.of("java.lang.String", "ch.frankel.blog.Dummy", "java.util.ArrayList")
.map(it ->new ForNamer().apply(it)) // 1
.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;
}
}
}
- 创建一个单独的record对象。
- 重用它。
项目Lombok是一个编译时注释处理器,它会生成额外的字节码。使用正确的注释就可以得到结果,而无需编写样板代码。
- Project Lombok是一个Java库,它可以自动插入到您的编辑器和构建工具中,为您的Java增添风味。再也不要写getter或equals方法了,只需一个注释,你的类就有了功能齐全的构建器,自动化你的日志记录变量等等。
Lombok提供了@SneakyThrow注释:它允许抛出已检异常,而无需在方法签名中声明它们。然而,目前它并不适用于现有的API。
如果你是Lombok用户,请注意,状态停放有一个打开的GitHub问题。
Commons Lang 的救援Apache Commons Lang是一个古老的项目。它在当时很流行,因为它提供了一些本可以成为Java API一部分的实用程序,但却没有。这比在每个项目中重新发明你的DateUtils和StringUtils要好得多。在研究这篇文章时,我发现它仍然使用很棒的api定期维护。其中之一是可失败的API。
该API由两部分组成:
- 一个包装器
Stream
- 签名接受异常的管道方法
这是一小段摘录:
代码最终变成了我们从一开始就期望的样子:
Streamstream = Stream.of("java.lang.String", "ch.frankel.blog.Dummy", "java.util.ArrayList");
Failable.stream(stream)
.map(Class::forName) // 1
.forEach(System.out::println);
仅仅修复编译时错误是不够的上述代码在运行时抛出ClassNotFoundException异常,并将其封装在UndeclaredThrowableException中。我们满足了编译器的要求,但是我们没有办法指定预期的行为:
- 处理第一个异常
- 丢弃的异常
- 聚合类和异常,以便我们可以在管道的最后阶段对它们采取行动
- 其他的东西
为了实现这一点,我们可以利用Vavr的力量。Vavr是一个将函数式编程的强大功能引入Java语言的库:
- Vavr core是一个Java函数式库。它有助于减少代码量并提高健壮性。迈向函数式编程的第一步是从不可变值开始思考。Vavr提供了不可变集合以及操作这些值所需的函数和控制结构。结果是美丽的,只是工作。
假设我们需要一个管道来收集异常和类。下面是该API的一个片段,描述了几个构建块:
它翻译成以下代码:
Stream.of("java.lang.String", "ch.frankel.blog.Dummy", "java.util.ArrayList")
.map(CheckedFunction1.liftTry(Class::forName)) // 1
.map(Try::toEither) // 2
.forEach(e ->{
if (e.isLeft()) { // 3
System.out.println("not found:" + e.getLeft().getMessage());
} else {
System.out.println("class:" + e.get().getName());
}
});
- 将这个调用封装到一个Vavr Try中。
- 将Try转换为Either,保留异常。如果我们不感兴趣,可以使用Optional对象。
- 根据Either是否包含异常(左)或预期结果(右)来采取行动。
到目前为止,我们都在Java流的世界里。它按预期工作,直到forEach,这看起来并不“漂亮”。
Vavr确实提供了自己的Stream类,它模仿了Java的Stream API并添加了额外的功能。让我们用它来重写管道:
var result = Stream.of("java.lang.String", "ch.frankel.blog.Dummy", "java.util.ArrayList")
.map(CheckedFunction1.liftTry(Class::forName))
.map(Try::toEither)
.partition(Either::isLeft) // 1
.map1(left ->left.map(Either::getLeft)) // 2
.map2(right ->right.map(Either::get)); // 3
result._1().forEach(it ->System.out.println("not found: " + it.getMessage())); // 4
result._2().forEach(it ->System.out.println("class: " + it.getName())); // 4
- 将Either的流划分为两个流组成的元组。
- 将左边的流从任一流压平,变成可抛掷的流。
- 将右边的stream从Either的stream展平为Class的stream。
- 想做什么就做什么。
Java最初的设计大量使用了已检异常。编程语言的发展证明了这不是一个好主意。
Java流不能很好地处理已检异常。将后者集成到前者所需的代码看起来并不好。为了恢复流的可读性,我们可以使用Apache Commons Lang。
编译只是问题的一小部分。我们通常希望对异常采取行动,而不是停止管道或忽略异常。在这种情况下,我们可以利用Vavr库,它提供了一种更函数式的方法。
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
网页题目:Lambda表达式中的异常-创新互联
网站路径:http://scpingwu.com/article/dgepep.html