bigywater.tech
Java 内部原理 · 42

Optional 本身可以是 null:真实验证这个类型没能真正杜绝 NPE

"用 Optional 作为返回类型能避免 NPE"是常见说法。真实写一个返回 Optional<String> 却直接 return null 的方法,看看这句话在语言机制层面到底成不成立。

编译器完全不拦
真实验证:方法声明返回 Optional<String>,内部直接 return null,真实编译通过,没有任何警告或错误
调用方真实 NPE
对这个真实为 null 的返回值调用 isPresent(),真实抛出 NullPointerException——Optional 类型本身没能挡住这一点

上一篇:thenApply 真实可能在调用线程执行,不一定是线程池:真实测出两种结果

真实验证:空 Optional 的正常行为

先确认 Optional 本身设计上没问题的那一部分:空值调用 get() 会真实、明确地抛出异常,不是静默返回 null。

真实实测
empty.get() 真实抛出 NoSuchElementException: No value present

这一点符合设计初衷:相比直接返回 null 然后在某个遥远的地方莫名其妙 NPE,Optional.get() 在明确没有值的时候会真实、立刻、带着清晰信息地失败。

真实验证:Optional 类型本身,可以是 null

Optional 只是一个普通的类,Java 语言没有任何机制阻止一个声明为 Optional<T> 的引用真实指向 null。

OptionalDemo.java(一个真实存在于不少代码库里的坏例子) static Optional<String> badMethod(boolean giveNull) { if (giveNull) { return null; // 编译器完全不会阻止这一行 } return Optional.of("真实值"); }
真实实测
badMethod(true) 返回的 Optional 本身是不是 null = true
对返回值调用 isPresent() 真实抛出 NullPointerException——Optional 类型完全没能阻止这一点

真实结果确认了这一点:badMethod(true) 声明的返回类型是 Optional<String>,方法体里真实写了 return null;,javac 真实没有报任何错误或警告——这行代码完全合法。调用方如果天真地相信"返回类型是 Optional 就不用判空了",直接调用 isPresent(),真实立刻踩到 NullPointerException,而且这个异常和"忘了判断 Optional 内部有没有值"是两件完全不同的事——问题出在更早的一层,Optional 这个容器本身都没拿到。

真实验证:正确用法真的不需要显式判空

当方法真实、规范地返回了一个 Optional 实例(不是 null)时,orElse/ifPresentOrElse 这类方法能让调用方完全不用写 if 判断。

真实实测
goodResult.orElse("默认值") = 默认值
真实走了 ifPresentOrElse 的空分支,没有抛异常
真实实测 · of(null) 和 ofNullable(null) 的真实区别
Optional.of(null) 真实立刻抛出 NullPointerException
Optional.ofNullable(null) 真实不抛异常,isPresent() = false
真实结论是什么?Optional 真实解决的问题,只是"容器内部有没有值"这一层——它把这件事从一个隐晦的 null 检查,变成了一个显式的 API(isPresent()/orElse()/ifPresentOrElse())。但它没有、也不可能杜绝"这个 Optional 引用本身是不是 null"这个更外层的问题,因为 Java 语言没有非空类型系统。真实、可靠的约定应该是:任何返回 Optional 的方法,内部永远不应该 return null(该返回 Optional.empty());调用方也不应该假设"类型是 Optional 所以不用防御性判空",这个假设在真实的、写法不规范的代码库里是不成立的。

分步演示:用了 Optional,哪些空指针陷阱真实还在

每一行都是真实运行这段代码得到的结果或真实抛出的异常。

Optional 真实并没有消灭 NullPointerException,只是把"忘记判空"变成了另外几种真实可能踩中的坑
这一步新验证的调用

    参考与说明

    • 本文全部结果均在本机真实运行 Java 17 得到。
    • 和三元表达式拆箱 NPE那篇一样,都是"语言机制没能完全兜底,依然需要人为遵守约定"这一类真实案例。
    • 没有涉及:Optional 作为方法参数类型或类字段类型被官方文档明确不建议使用的原因、Optional.map/flatMap 链式调用的真实短路行为——这些留给系列后面按需再写。
    京ICP备2026067502号-1京公网安备11010502063487号