Java中满足referential transparency要求的代码改造方法
原代码不满足引用透明性的核心原因
Java 中List.remove(int index)是带副作用的可变操作,天然不符合引用透明的要求,核心问题有两点:
- 方法调用会直接修改原列表的内部结构:移除指定索引的元素后,后续位置的元素会自动向前补位,列表长度同步减1,属于可被外部感知的状态变更
- 方法的返回值不固定:同样入参传入索引1,第一次调用和第二次调用拿到的元素完全不同,结果完全依赖调用发生时列表的实时状态,而不是仅由传入参数决定
举个实际运行的对照:假设初始numberList为[10,20,30,40]
- 计算
first的过程:先调用remove(1)拿到20,此时列表变为[10,30,40],再调用remove(2)拿到40,最终first=60,列表剩余元素为[10,30] - 直接在上面的剩余列表基础上计算
second:此时列表索引最大为1,调用remove(2)会直接触发数组越界异常
就算每次计算前都把列表重置回初始状态,两个值也不相等:重置列表为[10,20,30,40]后计算second,先调用remove(2)拿到30,此时列表变为[10,20,40],再调用remove(1)拿到20,最终second=50,和之前first=60的结果完全不一致。
符合引用透明性要求的改造方法
改造的核心思路是彻底剥离原代码中带副作用的修改操作,所有计算都基于无副作用的读操作完成,确保两个计算路径拿到的元素值完全一致。
具体改造步骤:
- 提前通过无副作用的
List.get(int index)方法,从初始列表中固定取出索引1、索引2对应的元素值,整个读操作过程完全不会修改原列表的结构 - 用取出的两个固定值做加法计算,不管两个加数的书写顺序如何,最终计算结果完全一致
改造后的参考代码:
// 全程无修改操作,直接从原始列表读取固定位置的元素 int elementAt1 = numberList.get(1); int elementAt2 = numberList.get(2); // 两个表达式的取值完全相等,满足引用透明要求 int first = elementAt1 + elementAt2; int second = elementAt2 + elementAt1;
引用透明的核心判定标准:
只要是引用透明的表达式,不管在代码的哪个位置被调用、被替换成它的返回值,程序的最终行为都不会发生任何变化。这种特性下,计算过程完全可以被缓存、并行执行,不存在可变状态带来的不确定性。
内容的提问来源于stack exchange,提问作者newToThisStuff
相关产品推荐
相关产品推荐

