核心是用合适数据结构强化参数契约、显式表达意图、隔离不确定性:用optional封装可选参数,不可变容器承载默认值与校验后参数,builder模式结构化多参数初始化,枚举+策略映射替代null分支判断。
核心不是靠数据结构“兜底”,而是用合适的数据结构强化参数契约、显式表达意图、隔离不确定性。关键在于让未初始化状态可识别、可传递、不可误用。
用 Optional 封装可能为空的输入参数
当某个参数天然允许不提供(比如可选回调、备用配置),不要让它以 raw null 形式流入方法体,而应统一接收 Optional
- 声明更诚实:public void process(Optional
policy) 明确告诉调用方“这个可以没有” - 避免空检查污染逻辑:policy.orElse(DefaultRetryPolicy.INSTANCE).apply() 比 if (policy != null) ... else ... 更紧凑安全
- 禁止反模式:不要写 return Optional.ofNullable(input),除非 input 本就语义上可能缺失;若它本该必填,就该在校验阶段报错,而非包装成 Optional 掩盖设计问题
用不可变容器承载默认值与校验后参数
对集合类参数(如 List、Map),拒绝接受 null 或裸 mutable 集合。入口处立即转为线程安全、不可变、有边界的结构:
- 字符串列表:Assert.notNull(tags, "tags must not be null"); List
safeTags = Collections.unmodifiableList(new ArrayList(tags)) - 配置映射:用 Map.copyOf(configMap) 替代原始 map 引用,防止外部篡改影响工具类内部状态
- 优先选用 Guava 的 ImmutableXXX 或 JDK9+ 的 List.of() / Map.ofEntries(),它们在构造时即校验 null 元素,失败抛 NPE,fail-fast 更早
用 Builder 模式结构化多参数初始化
当工具类构造依赖多个可选/必填参数(尤其含嵌套对象),避免堆砌带大量 null 默认值的构造函数。改用 Builder + 内部不可变数据结构:
- Builder 内部用 final 字段暂存已设参数,未设字段保持 null 或 Optional.empty()
- build() 方法集中校验:比如 “host 和 port 必须同时非空”,“timeoutMs > 0”,错误信息可包含缺失项完整路径
- build() 返回一个 private static final class Config { … },所有字段 final,无 setter,彻底杜绝后续修改或未初始化访问
用枚举 + 策略映射替代 null 分支判断
当参数代表行为选择(如序列化格式、重试策略类型),与其传 String 或 Class 或 null 再做 if-else,不如定义明确枚举,并预置对应策略实例:
- enum SerializationType { JSON, PROTOBUF, AVRO }
- 每个枚举值持有一个 lazy 初始化的 Serializer 实例(用 Supplier 包装),null 不再是合法输入,未注册类型直接抛 IllegalArgumentException
- 调用方传 SerializationType.JSON,工具类直接 getSerializer(),无需判空、无需反射、无运行时类型风险











