学习 Java 业务开发时,默认值经常被当成一个很小的实现细节。字段没有传进来,就补一个零;字符串没有内容,就换成空字符串;配置没有填写,就沿用某个固定数字。这样确实能让程序继续执行,但继续执行并不代表含义正确。今天想整理的,是默认值如何改变输入的解释,以及怎样避免它悄悄隐藏本来需要处理的问题。

可以先想象一个修改商品库存的请求。调用方没有提供库存数量,与明确提供数量为零,可能代表完全不同的意图。前者可以表示不修改这个字段,后者则表示把库存调整为零。如果在读取请求时统一补零,后面的业务逻辑就再也无法区分两种情况。程序没有报错,却已经把调用方没有表达的意思写进了数据。

因此,选择默认值之前,要先确定缺失意味着什么。它可能表示使用系统约定,也可能表示保持原值,还可能代表输入不完整,需要拒绝请求。不同场景的答案未必相同。创建对象时没有填写备注,可以接受为空;修改对象时没有提交备注,可能表示保持原来的内容。不能因为字段名称相同,就让所有入口共享同一种补值规则。

零值尤其容易造成误解。金额为零可能表示免费,也可能是计算尚未完成;耗时为零可能是真正接近零,也可能是没有采集到数据;数量为零可能是库存耗尽,也可能是查询失败后被随手填入。把这些状态都压缩成同一个数字,会让后续统计和判断失去依据。若业务需要区分,就应该保留能够表达差异的信息,而不是等到展示时再尝试猜测。

字符串也有类似问题。未提供、空字符串、只有空格,在某些输入中可以统一解释,在另一些输入中却需要区别对待。是否去除空格、是否把空内容当作清空,都应该由字段的业务含义决定。特别是在局部更新场景,清空一个已有值通常是一项真实操作,需要有明确表达,不能由某次格式整理意外触发。

配置中的默认值还会影响运行行为。例如一次任务没有指定批次大小,系统可以采用一个经过约定的值;但如果某个必须设置的外部地址没有填写,默默选择另一个地址就可能把问题扩大。哪些配置允许回退,哪些配置缺失时必须停止启动,应当明确区分。默认值的存在应该让合法的简单场景更方便,而不是让错误配置以看似正常的方式进入运行阶段。

同样需要考虑默认值何时生效。入口补值、业务处理时补值和持久化时补值,可能让不同路径得到不同结果。如果网页入口做了补值,而内部任务没有做,相同的业务操作就可能出现不同解释。更清楚的做法,是确定规则属于哪一层,在相应边界统一兑现,并让其他层知道拿到的数据是否已经完成解释。

默认规则也可能随业务变化。今天未填写配送方式时默认普通配送,未来可能根据地区或商品类型选择不同方式。若只保存最终值而没有保留必要的上下文,后来就可能难以解释当时为何作出这个选择。是否需要记录默认规则的来源、是否保存用户明确选择过的事实,应当结合追溯需求决定,不能一概而论。

对外返回结果时,还要避免用默认值掩盖不可用状态。某个辅助服务查询失败,直接返回零,可能让调用方误以为业务结果确实为零。更可靠的接口约定应该说明哪些字段可能暂不可用,以及调用方如何区分正常结果与缺失信息。默认展示可以方便阅读,但不能替代底层事实的准确表达。

我觉得一个实用的学习练习,是给同一个字段分别准备未提供、明确为空、明确为零和正常取值四种输入,再写出每种输入应该产生的行为。接着检查经过转换、校验和保存以后,这些差异是否仍然按约定保留。默认值看似只是一个赋值动作,实际却是在替业务作出解释。把这种解释讲清楚,往往比增加更多防空判断更有价值。