引言 💡
在上一篇Java进阶篇之Stream API中,我们介绍了集合的筛选与转换。调用findFirst()时,集合可能为空,也可能没有匹配项。这时,方法返回的Optional就值得仔细看看。
查询用户昵称、查找课程、读取可选配置,都有"可能找不到"的情况。今天,我们通过一个昵称处理示例,学习怎样把这条分支表达清楚。
版本说明:本文使用Java 8的API,可在Java 17中使用。示例输出为预期输出。
一、用Optional表达可选结果 🧩
Optional表示一个可能包含值的容器。有值时可以继续转换;没有值时,可以选择默认值或抛出明确的异常。它本身应始终是有效对象,返回Optional的方法应使用Optional.empty()表示无结果。
| 创建方式 | 用途 |
|---|---|
| Optional.of(value) | 已确定值非null |
| Optional.ofNullable(value) | 接收可能为null的值 |
| Optional.empty() | 明确表示没有结果 |
二、从昵称清理开始 📝
下面的程序保存为OptionalDemo.java,可以完整编译运行。
java
import java.util.Optional;
public class OptionalDemo {
static String displayName(String nickname) {
return Optional.ofNullable(nickname)
.map(String::trim)
.filter(name -> !name.isEmpty())
.orElse("访客");
}
public static void main(String[] args) {
System.out.println(displayName(" 小明 "));
System.out.println(displayName(" "));
System.out.println(displayName(null));
}
}
预期输出:
text
小明
访客
访客
这里有三个值得留意的步骤:先接住可能为空的输入,再清理空格,最后排除空字符串。调整步骤顺序之前,先想想每一步接收到的是什么数据。
三、默认值怎么准备?🔍
orElse 适合直接提供现成默认值;orElseGet可以在缺少结果时再执行准备默认值的函数。
java
import java.util.Optional;
public class OptionalDefaultDemo {
static String fallback() {
System.out.println("准备默认值");
return "访客";
}
public static void main(String[] args) {
Optional<String> name = Optional.of("小明");
System.out.println(name.orElse(fallback()));
System.out.println(name.orElseGet(OptionalDefaultDemo::fallback));
}
}
预期输出:
text
准备默认值
小明
小明
第一处调用需要先计算参数,因此fallback()执行了。第二处传入方法引用,已有昵称时无需准备默认值。涉及查询、文件读取等有成本的工作时,这个区别尤其值得注意。
四、接上Stream的查询结果 🔗
以下片段可放入main方法,并导入java.util.Arrays:
java
String course = Arrays.asList("Java基础", "数据库基础")
.stream()
.filter(name -> name.startsWith("Python"))
.findFirst()
.orElse("暂无匹配课程");
System.out.println(course);
预期输出是"暂无匹配课程"。筛选条件明确后,空结果的处理紧接在查询末尾,阅读时更容易看清完整流程。
五、使用时留意这些细节 ⚠️
谨慎调用get():空Optional调用get()会抛出NoSuchElementException。根据业务需求选择默认值、ifPresent()或带异常供应器的orElseThrow(),让无结果时的行为更明确。
区分map与flatMap:转换函数返回普通值时使用map;如果已经返回Optional,可以使用flatMap衔接,避免形成多层Optional。flatMap的转换函数应返回有效Optional,不能返回null。
保留业务含义:昵称缺失时显示"访客"很自然;订单不存在时直接生成默认订单,可能掩盖真实问题。默认值应由业务规则决定。
🧠 思维导图
#mermaid-svg-Nt9Jd6HbuiIqdDt4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .error-icon{fill:#552222;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .marker.cross{stroke:#333333;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 p{margin:0;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .cluster-label text{fill:#333;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .cluster-label span{color:#333;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .cluster-label span p{background-color:transparent;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .label text,#mermaid-svg-Nt9Jd6HbuiIqdDt4 span{fill:#333;color:#333;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .node rect,#mermaid-svg-Nt9Jd6HbuiIqdDt4 .node circle,#mermaid-svg-Nt9Jd6HbuiIqdDt4 .node ellipse,#mermaid-svg-Nt9Jd6HbuiIqdDt4 .node polygon,#mermaid-svg-Nt9Jd6HbuiIqdDt4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .rough-node .label text,#mermaid-svg-Nt9Jd6HbuiIqdDt4 .node .label text,#mermaid-svg-Nt9Jd6HbuiIqdDt4 .image-shape .label,#mermaid-svg-Nt9Jd6HbuiIqdDt4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .rough-node .label,#mermaid-svg-Nt9Jd6HbuiIqdDt4 .node .label,#mermaid-svg-Nt9Jd6HbuiIqdDt4 .image-shape .label,#mermaid-svg-Nt9Jd6HbuiIqdDt4 .icon-shape .label{text-align:center;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .node.clickable{cursor:pointer;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .arrowheadPath{fill:#333333;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .cluster text{fill:#333;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .cluster span{color:#333;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .icon-shape,#mermaid-svg-Nt9Jd6HbuiIqdDt4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .icon-shape p,#mermaid-svg-Nt9Jd6HbuiIqdDt4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .icon-shape .label rect,#mermaid-svg-Nt9Jd6HbuiIqdDt4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Nt9Jd6HbuiIqdDt4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Optional
创建结果
of、ofNullable、empty
转换与筛选
map、flatMap、filter
处理缺失
默认值、延迟计算、明确异常
使用细节
避免盲目get、保留业务含义
六、总结 🎯
总结要点
结果表达:Optional把"可能没有结果"带入方法返回值,调用方可以沿着处理流程明确安排后续行为。
默认值选择:现成的简单默认值可以直接提供;需要执行额外操作才能获得默认值时,先考虑是否应延迟执行。
落到业务:集合查找、昵称清理和可选配置都有不同的缺失含义。写代码之前先确定规则,处理链才能简洁且容易维护。
在接下来的文章中,我们将继续探讨Java中的日期与时间API,学习常见的时间表示与转换,敬请期待!
👉 如果你觉得这篇文章对你有所帮助,欢迎点赞、收藏、分享!😊