空对象模式解决方法返回null导致调用方频繁判空的问题,它通过提供不抛异常、无副作用、统一接口的哑实现来替代null,但仅适用于null无业务语义的场景。

空对象模式解决什么问题
当方法可能返回 null,而调用方又得反复判空、否则抛 NullPointerException 时,空对象模式就该出现了。它不是绕开 null,而是把“无意义的空”变成“有意义的哑实现”——比如返回一个不做任何事的 Logger,而不是 null,下游直接 log.info() 就行,不用每处都 if (logger != null)。
怎么写一个真正的空对象
关键不是“new 出来一个空实例”,而是让这个实例在所有公共行为上表现安全、可预测、不改变状态。
- 必须和真实类实现同一接口(或继承同一抽象类),不能只靠约定
- 所有返回值方法返回“零值”:
String返回"",int返回0,集合返回Collections.emptySet(),绝不返回null - 所有带副作用的方法(如
save()、send())直接空实现,不抛异常、不打日志、不调远程 - 避免在空对象里藏逻辑分支,比如“如果配置开启就打印 warn”,这会让它的行为不可预期
示例:一个空的 UserRepository
public class NullUserRepository implements UserRepository {
@Override
public User findById(Long id) {
return null; // 注意:这里仍返回 null —— 如果业务契约允许“查无此人”为 null,那空对象不该强行改契约
}
@Override
public List<User> findAll() {
return Collections.emptyList(); // ✅ 安全返回
}
@Override
public void save(User user) {
// ✅ 空实现,不抛异常
}
}
什么时候不该用空对象
它不是万能解药,滥用反而掩盖问题。
立即学习“Java免费学习笔记(深入)”;
- 当
null本身是有效业务信号时(如“用户未登录”“配置未加载”),硬塞空对象会丢失语义,下游无法区分“真没数据”和“假装有但啥也不干” - 当接口方法太多、且部分方法本就不该被空对象支持(比如
lock()或rollback()),实现起来成本高、易出错 - 在需要严格类型安全的场景(如 Kotlin 的非空类型),空对象可能绕过编译检查,让潜在问题延后暴露
Spring 中怎么安全替换 null 返回值
Spring 本身不内置空对象自动注入机制,得靠手动控制 bean 创建逻辑。
- 别依赖
@Nullable+@Autowired组合——Spring 不会因为你标了@Nullable就给你塞个空对象 - 推荐方式:用
@Primary配合条件化 bean,比如测试环境注入NullUserService,生产环境注入真实实现 - 更稳妥的做法是封装一层工厂或策略:由
UserServiceProvider根据上下文决定返回真实实现还是空对象,而不是让调用方自己猜 - 注意:如果方法签名返回
Optional<user></user>,那就别再套空对象——Optional.empty()已经是更轻量、更标准的“可选但为空”表达
空对象真正的难点不在写法,而在判断“这个空,到底该不该有行为”。很多人一上来就做空实现,结果发现下游其实依赖了 null 来触发降级逻辑,最后只能回滚。









