查看: 13698|回复: 0

Java Functional - 可选链

[复制链接]
发表于 2021-9-20 03:32:26 | 显示全部楼层 |阅读模式
本帖最后由 by鬼神 于 2021-9-20 03:36 编辑

介绍
在 Java 8 之前,方法必须抛出异常或 return null,这两种方法都不是完美的。Java 8 中引入了 Optional、OptionalInt、OptionalLong 和 OptionalDouble 来表示可能是null.

Optionals 有两个内部状态,empty或present。如果基础引用为 ,则 Optional 为空null。当基础引用不为空时,存在 Optional。

尽管有多种使用 Optional 的方法,但链式可选项通常用于编写清晰简洁的代码,尤其是在需要复杂过滤时。

目标
在本教程结束时,您将学到:

  • 如何使用可选方法map()和filter().

必备知识
  • 爪哇 8.
  • Java 函数概念:可选,方法引用。


所需工具
  • 至少支持 JDK 8 的 Java IDE。

项目设置
要按照本教程进行操作,请执行以下步骤:

  • 创建一个新的空 Java 8+ 项目。
  • 创建一个新包com.example。
  • 创建一个名为 的新 Java 类Entry。
  • main()在Entry类中创建方法。
  • 在Entry类中创建两个 LocalDate 常量,如下所示:


[Java] 纯文本查看 复制代码
 private static final LocalDate GEN_ALPHA = LocalDate.ofYearDay(2010,1); //1
 private static final LocalDate GEN_Z = LocalDate.ofYearDay(1997, 1); //2


  • 添加一个方便的方法来检查 LocalDate 实例是否被视为 Gen Z。


[Java] 纯文本查看 复制代码
private static boolean isGenZ(LocalDate d){ //7
    return (d.isEqual(GEN_Z) || d.isAfter(GEN_Z)) && d.isBefore(GEN_ALPHA);
 }


项目说明
我们的项目很容易理解。第 1 行和第 2 行声明的两个 LocalDate 常量代表 2 个人口统计群组的开始日期:Alpha 和 Z 世代。第 7 行声明的便捷方法包含检查日期(生日)是否可以归类为 Z 世代的逻辑。

如何不使用 Optional
如果Optional#get不检查 Optional 是否存在(或非空),则永远不应直接使用该方法。如果Optional#get直接调用,程序在运行时会抛出 NoSuchElementException。

在 Entry 类中添加此方法。

[Java] 纯文本查看 复制代码
private static void noCheckOpt(){ //8
   Optional<LocalDate> opt = Optional.empty();

   LocalDate ogDate = opt.get(); //9

   if(isGenZ(ogDate)){
       LocalDate modifiedDate = ogDate
               .plusYears(1)
               .plusMonths(5)
               .plusDays(10);
       System.out.println(modifiedDate);
   }
}


上面的方法尝试从 Optional 获取底层 LocalDate 对象,然后仅当ogDate是 Gen Z 日期时才尝试在 if 块中创建新日期。

代码片段特意实例化了一个空的 Optional 来模拟从 API 接收到的 Optional 对象可以为空的情况。专业代码永远不应该这样写。

从 中调用上面的方法后main(),我们可以看到代码按预期抛出了 NoSuchElementException。

基本可选值检查
为了避免程序抛出 NoSuchElementException,开发人员可以做的最基本的事情是至少检查 Optional 对象是否包含带有Optional#isEmpty或的值Optional#isPresent。

下面的方法添加了存在检查。

[AppleScript] 纯文本查看 复制代码
private static void checkOpt(){ //10
   Optional<LocalDate> opt = Optional.empty(); //11

   if(opt.isPresent()){ //12
       LocalDate ogDate = opt.get(); //13
       if(isGenZ(ogDate)){ //14
           LocalDate modifiedDate = ogDate //15
                   .plusYears(1)
                   .plusMonths(5)
                   .plusDays(10);
           System.out.println(modifiedDate);
       }
   }
}


如果我们注释掉对前一个方法的调用,然后再调用这个方法,我们的代码应该不会再抛出任何异常了。它发现 Optional opt 在第 12 行为空,因此它停止执行该方法的其余部分。

但是这个方法有问题。它非常冗长。由于必须检查是否可选为空及是否底层是LOCALDATE第二代Z,我们现在有嵌套if块。我们还添加了两个变量声明。

可选链
为了提高可读性,我们可以使用内置的 Optional 方法Optional#filter,Optional#map就像下面的代码片段。

[Java] 纯文本查看 复制代码
private static void optChain(){ //16
   Optional.<LocalDate>empty() //17
           .filter(Entry::isGenZ) //18
           .map(d -> d.plusYears(1).plusMonths(5).plusDays(10)) //19
           .ifPresent(System.out::println); //20
}


要了解此方法的工作原理,让我们从头到尾回顾一下之前的方法在做什么:

  • 检查Optional 是否为空。
  • 检查底层 LocalDate 对象是否为 Gen Z。
  • 如果是 Z 世代,则对其进行改造。
  • 打印出 LocalDate。

该filter()和map()方法自动执行可选空虚检查对我们来说,和下游返回一个空的可选对象,所以我们没有写检查自己。只有在最后一步我们才需要调用ifPresent(),并且ifPresent()肯定比手动if块更具可读性,因此它增加了可读性。

使用filter(),map()和 i 的另一个额外好处fPresent()是我们可以将 lambda 或方法引用传递给它们,从而进一步提高可读性。

解决方案代码

[Java] 纯文本查看 复制代码
    package com.example;

    import java.time.LocalDate;
    import java.util.Optional;

    public class Entry {
       private static final LocalDate GEN_ALPHA = LocalDate.ofYearDay(2010,1); //1
       private static final LocalDate GEN_Z = LocalDate.ofYearDay(1997, 1); //2

       public static void main(String... args){ //3
           //noCheckOpt(); //4
           //checkOpt(); //5
           //optChain(); //6
       }

       private static boolean isGenZ(LocalDate d){ //7
           return (d.isEqual(GEN_Z) || d.isAfter(GEN_Z)) && d.isBefore(GEN_ALPHA);
       }

       private static void noCheckOpt(){ //8
           Optional<LocalDate> opt = Optional.empty();

           LocalDate ogDate = opt.get(); //9

           if(isGenZ(ogDate)){
               LocalDate modifiedDate = ogDate
                       .plusYears(1)
                       .plusMonths(5)
                       .plusDays(10);
               System.out.println(modifiedDate);
           }
       }

       private static void checkOpt(){ //10
           Optional<LocalDate> opt = Optional.empty(); //11

           if(opt.isPresent()){ //12
               LocalDate ogDate = opt.get(); //13
               if(isGenZ(ogDate)){ //14
                   LocalDate modifiedDate = ogDate //15
                           .plusYears(1)
                           .plusMonths(5)
                           .plusDays(10);
                   System.out.println(modifiedDate);
               }
           }
       }

       private static void optChain(){ //16
           Optional.<LocalDate>empty() //17
                   .filter(Entry::isGenZ) //18
                   .map(d -> d.plusYears(1).plusMonths(5).plusDays(10)) //19
                   .ifPresent(System.out::println); //20
       }

    }


概括
手动检查 Optional 的存在是有效的,但是有很多方便的方法可以与 lambda 和方法引用糖语法一起使用。当然,查找它们确实需要花费一些额外的时间,但是可读性的好处是值得的。


我叫by鬼神。我是一名 Java 开发人员,专门从事 Java/Spring/MySQL 堆栈的后端开发。

我还可以使用 Angular/Typescript/JS/HTML/CSS 和带有 Kotlin 的原生 Android 在前端工作。

我以后会分享更多专业知识和资源到中国红客联盟与你们一起探讨

如果有其他问题可以在下方评论,我上线会一一解答
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

旗下站点

邮箱系统

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2025-3-7 03:14 , Processed in 0.018336 second(s), 9 queries , Gzip On, MemCache On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部