Optional核心是将“值是否存在”变为类型契约,显性化空值语义,支持可组合、可推演的函数式处理;它不替代所有判空,而应封装在领域层方法中返回,禁用作字段或参数,慎用orElseThrow。

用Optional封装业务逻辑中的可选结果,核心是把“值是否存在”变成类型契约的一部分,而不是靠if-else去猜。它不是为替代所有判空而生,而是让“可为空”这个语义显性化、可组合、可推演。
把空值处理下沉到领域层
不要在Service或Controller里反复写Optional.ofNullable(user).map(...).flatMap(...)。应该在实体或领域对象中提供语义清晰的方法:
-
User::safeGetAddress()→ 返回Optional,内部封装了user != null && user.getAddress() != null -
Order::getLatestPaymentTime()→ 返回Optional,不暴露原始字段,也不暴露null判断细节 - 业务代码直接链式调用:
order.getLatestPaymentTime().filter(t -> t.isAfter(weekAgo)).isPresent(),上层只关心“有没有符合条件的支付时间”,不操心怎么来的
慎用orElseThrow,优先选orElse或orElseGet
绝大多数场景下,空值不等于系统异常,只是业务路径的一种分支:
- ❌ 避免:
.orElseThrow(() -> new RuntimeException("地址为空"))——破坏Optional设计初衷,且抛出泛型异常难以定位 - ✅ 推荐:
.orElse("北京"),默认值明确、轻量、无副作用 - ✅ 推荐:
.orElseGet(this::fetchDefaultCity),适合需要查库、计算或IO的延迟逻辑 - ⚠️ 仅当空值代表严重业务违例(如订单无收货地址无法履约)时,才用
orElseThrow(NoDeliveryAddressException::new),并配具体业务异常类型
用map/flatMap组合多层嵌套结构
面对User → Address → City → Code这类长链取值,传统方式要写4层if;Optional用函数式链即可自然短路:
-
Optional.ofNullable(user)启动链式入口 -
.map(User::getAddress).map(Address::getCity).map(City::getCode)每一步都自动跳过null,无需手动判空 - 最后接
.orElse("CN")或.ifPresent(this::logCityCode),收口干净 - 注意:避免在map里做副作用操作(比如发消息、改状态),保持函数纯度
避免Optional作为字段或参数传递
Optional是“计算过程中的容器”,不是数据模型的一部分:
- ❌ 不要在Entity、DTO、VO中声明
private Optional——序列化、ORM、JSON解析都会出问题name; - ❌ 不要把Optional当方法参数:
void process(Optional,这会让调用方困惑“为什么我传个Optional进来?”user) - ✅ 正确姿势:方法返回Optional(表示“可能找不到”),入参保持原始类型(
User user),由调用方决定是否包装 - ✅ 工具类中可提供
OptionalUtil.safeGet(User::getAddress)这类静态辅助,但别滥用










