请选择 进入手机版 | 继续访问电脑版
查看: 1365|回复: 0

Java Functional - 可选链

[复制链接]
  • TA的每日心情
    开心
    7 天前
  • 签到天数: 41 天

    [LV.5]常住居民I

    发表于 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 在前端工作。

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

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

    使用道具 举报

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

    本版积分规则

    快速回复 返回顶部 返回列表