189 8069 5689

Lambda表达式中的异常-创新互联

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 Classapply(String string) {
        try {
            return Class.forName(string);
        } catch (ClassNotFoundException e) {
            return null;
        }
    }
}
  1. 创建一个单独的record对象。
  2. 重用它。
Trying With Lombok

项目Lombok是一个编译时注释处理器,它会生成额外的字节码。使用正确的注释就可以得到结果,而无需编写样板代码。

  • Project Lombok是一个Java库,它可以自动插入到您的编辑器和构建工具中,为您的Java增添风味。再也不要写getter或equals方法了,只需一个注释,你的类就有了功能齐全的构建器,自动化你的日志记录变量等等。

Lombok提供了@SneakyThrow注释:它允许抛出已检异常,而无需在方法签名中声明它们。然而,目前它并不适用于现有的API。

如果你是Lombok用户,请注意,状态停放有一个打开的GitHub问题。

Commons Lang 的救援

Apache Commons Lang是一个古老的项目。它在当时很流行,因为它提供了一些本可以成为Java API一部分的实用程序,但却没有。这比在每个项目中重新发明你的DateUtils和StringUtils要好得多。在研究这篇文章时,我发现它仍然使用很棒的api定期维护。其中之一是可失败的API。 

该API由两部分组成:

  1. 一个包装器Stream
  2. 签名接受异常的管道方法

这是一小段摘录:

代码最终变成了我们从一开始就期望的样子:

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());
          }
      });
  1. 将这个调用封装到一个Vavr Try中。
  2. 将Try转换为Either,保留异常。如果我们不感兴趣,可以使用Optional对象。
  3. 根据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
  1. 将Either的流划分为两个流组成的元组。
  2. 将左边的流从任一流压平,变成可抛掷的流。
  3. 将右边的stream从Either的stream展平为Class的stream。
  4. 想做什么就做什么。 
结论

Java最初的设计大量使用了已检异常。编程语言的发展证明了这不是一个好主意。

Java流不能很好地处理已检异常。将后者集成到前者所需的代码看起来并不好。为了恢复流的可读性,我们可以使用Apache Commons Lang。

编译只是问题的一小部分。我们通常希望对异常采取行动,而不是停止管道或忽略异常。在这种情况下,我们可以利用Vavr库,它提供了一种更函数式的方法。

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


分享标题:Lambda表达式中的异常-创新互联
链接地址:http://jkwzsj.com/article/dgepep.html

其他资讯