by鬼神 发表于 2021-9-20 03:32:26

Java Functional - 可选链

本帖最后由 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 常量,如下所示:


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。


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 类中添加此方法。

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。

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

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就像下面的代码片段。

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 或方法引用传递给它们,从而进一步提高可读性。

解决方案代码

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

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

如果有其他问题可以在下方评论,我上线会一一解答
页: [1]
查看完整版本: Java Functional - 可选链