本帖最后由 by鬼神 于 2021-9-20 03:36 编辑
介绍
在 Java 8 之前,方法必须抛出异常或 return null,这两种方法都不是完美的。Java 8 中引入了 Optional、OptionalInt、OptionalLong 和 OptionalDouble 来表示可能是null.
Optionals 有两个内部状态,empty或present。如果基础引用为 ,则 Optional 为空null。当基础引用不为空时,存在 Optional。
尽管有多种使用 Optional 的方法,但链式可选项通常用于编写清晰简洁的代码,尤其是在需要复杂过滤时。
目标
在本教程结束时,您将学到:
必备知识
所需工具
项目设置
要按照本教程进行操作,请执行以下步骤:
- 创建一个新的空 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 在前端工作。
我以后会分享更多专业知识和资源到中国红客联盟与你们一起探讨
如果有其他问题可以在下方评论,我上线会一一解答 |