
Java Optional 别再乱用:90% 的人都把它当空值检查了Optional从 Java 8 引入到现在,我见过太多这样的代码:OptionalUseroptfindUser(id);if(opt.isPresent()){Useruseropt.get();// ...}这写法能跑,但它把Optional用成了「换了张皮的 null 检查」,完全没发挥它的价值,还多了一层包装。今天讲清楚Optional到底该怎么用、哪些用法是反模式。Optional 的设计初衷Optional不是用来消灭所有 null 的,它的定位很窄:作为方法返回值,明确告诉调用者「这里可能没有结果」。它逼你在编译期就正视「值可能不存在」,而不是运行时吃一个NullPointerException。所以第一条原则:Optional 主要用在返回值上,不是拿来包装一切。反模式一:isPresent get开头那段代码就是最典型的反模式。它和if (x ! null)没有本质区别,还啰嗦。正确姿势是用函数式方法把逻辑串起来:// ❌ 反模式:换皮的 null 检查OptionalUseroptfindUser(id);if(opt.isPresent()){returnopt.get().getName();}returnunknown;// ✅ 用 map orElse,一行表达意图returnfindUser(id).map(User::getName).orElse(unknown);map会在值存在时转换、不存在时短路,orElse给个兜底。整条链读起来就是「找到用户就取名字,否则返回 unknown」,意图一目了然。反模式二:orElse 里放重方法orElse的参数无论值是否存在都会被求值。如果兜底值是个昂贵操作,会白白执行:// ❌ 即使 user 存在,createDefaultUser() 也会被调用!returnfindUser(id).orElse(createDefaultUser());// ✅ orElseGet 接受 Supplier,只在值不存在时才执行returnfindUser(id).orElseGet(()-createDefaultUser());规则很简单:兜底是常量用orElse,兜底要计算用orElseGet。查数据库、new 大对象这类,一律orElseGet。反模式三:字段和参数用 Optional// ❌ 别把 Optional 当字段类型publicclassOrder{privateOptionalCouponcoupon;// 不要这样}// ❌ 也别当方法参数publicvoidprocess(OptionalConfigconfig){}Optional没实现Serializable,当字段会给序列化埋雷;当参数则强迫调用方包一层,还不如直接重载方法或允许传 null。字段用普通类型,该 null 就 null;Optional 留给返回值。正确用法合集值不存在时抛异常——用orElseThrow,比get()语义清晰:UseruserfindUser(id).orElseThrow(()-newUserNotFoundException(id));条件过滤——filter不满足就变空:// 找到用户且已激活才返回,否则视为不存在returnfindUser(id).filter(User::isActive);链式取嵌套值——map层层深入,任意一层为空整条就是空,天然防 NPE:// 用户 → 地址 → 城市,任何一环没有都安全返回 unknownreturnfindUser(id).map(User::getAddress).map(Address::getCity).orElse(unknown);存在才执行副作用——ifPresent:findUser(id).ifPresent(u-log.info(found: {},u.getName()));一个真实的重构对比改造前,典型的防御式嵌套判空:publicStringgetCity(LonguserId){UseruseruserRepo.find(userId);if(user!null){Addressaddruser.getAddress();if(addr!nulladdr.getCity()!null){returnaddr.getCity();}}returnunknown;}改造后,findById返回OptionalUser(Spring Data JPA 原生支持),整段坍缩成一条链:publicStringgetCity(LonguserId){returnuserRepo.findById(userId).map(User::getAddress).map(Address::getCity).orElse(unknown);}七行嵌套变三行,还消除了所有手写判空。这才是Optional该带来的收益。小结Optional的定位是方法返回值,表达「可能没有结果」,不是全局 null 替代品。别用isPresent()get(),那是换皮判空;用map/filter/orElse把逻辑串成链。兜底是常量用orElse,要计算用orElseGet;要抛异常用orElseThrow,别用get()。别把Optional当字段类型或方法参数——序列化和调用方都受罪。记忆点:Optional 是返回值上的「可能没有」声明,配合 map/orElse 用才对;一旦写出 isPresentget,就说明你在拿它当 null 检查。