这节主要是讲 可变对象给编程带来的危害,所谓不可变对象,就是整个生命周期中不可变的对象(废话), e.g. : String
具体来说参见 Basic Java when we discussed snapshot diagrams
Risks of mutation
risk1:passing mutable values
看以下两段代码:
/** @return the sum of the numbers in the list */
public static int sum(List<Integer> list) {
int sum = 0;
for (int x : list)
sum += x;
return sum;
}
/** @return the sum of the absolute values of the numbers in the list */
public static int sumAbsolute(List<Integer> list) {
// let's reuse sum(), because DRY, so first we take absolute values
for (int i = 0; i < list.size(); ++i)
list.set(i, Math.abs(list.get(i)));
return sum(list);
}
// meanwhile, somewhere else in the code...
public static void main(String[] args) {
// ...
List<Integer> myData = Arrays.asList(-5, -3, -2);
System.out.println(sumAbsolute(myData));
System.out.println(sum(myData));
}
- 可变性带来了一个潜藏的bug(passing mutable objects around is a latent bug)
- 可读性非常不好,作为读者,见到
main
方法的第一印象应该是sumAbs()
是求绝对值,而sum
是求和,而这导致了一个非常深的bug - 破坏了 fail-fast 原则, 往往很久才能定位真正的bug
Safe from bugs? In this example, it’s easy to blame the implementer of sumAbsolute() for going beyond what its spec allowed. But really, passing mutable objects around is a latent bug. It’s just waiting for some programmer to inadvertently mutate that list, often with very good intentions like reuse or performance, but resulting in a bug that may be very hard to track down.
Easy to understand? When reading main(), what would you assume about sum() and sumAbsolute()? Is it clearly visible to the reader that myData gets changed by one of them?
那么怎么解决呢?
至少可以做到两点,
- 尽量在不可变的对象前加
final
修饰 - 会改变参数的时候,在规范(specfication)中说明
risk2: returning mutable values
先看下面一段代码:
/** @return the first day of spring this year */
public static Date startOfSpring() {
if (groundhogAnswer == null) groundhogAnswer = askGroundhog();
return groundhogAnswer;
}
private static Date groundhogAnswer = null;
这段代码在求春天的第一天的时候加了一个 cache, 并且return 了一个可变对象。
如果有如下的client 做以下调用:
// somewhere else in the code...
public static void partyPlanning() {
// let's have a party one month after spring starts!
Date partyDate = startOfSpring();
partyDate.setMonth(partyDate.getMonth() + 1);
// ... uh-oh. what just happened?
}
那么很显然以后再次调用 startOfSpring
原来的静态私有变量 groundhogAnswer
就被抛出了。
至少有两种解决方案可解决这个问题:
- 不用mutable 的
date
而是用imutable 的替代物: package java.time: LocalDateTime, Instant, - defensive copying pattern 也就是说返回的时候返回
groudAns
的副本
return new Date(groundhogAnswer.getTime());
不过第二种方法虽然解决的问题,可是在大多数情况下我们都只需要一个共享的 groudAns
因此会带来一些性能上的问题。
Aliasing is what makes mutable types risky
重点说一些这个可变对象多出引用的问题,如果可变对象仅在一个局部被一个变量引用,那么用可变对象肯定是没有什么危害的,可是大多数情况下,可变对象被多出引用,这样就导致了可变对象引入的bug
Useful immutable types
- he primitive types and primitive wrappers are all immutable. If you need to compute with large numbers, BigInteger and BigDecimal are immutable.
- Don’t use mutable
Date
s, use the appropriate immutable type from java.time based on the granularity of timekeeping you need. - To create immutable collections from some known values, use [List.of](https://docs.oracle.com/javase/10/docs/api/java/util/List.html#of(E...), Set.of, and Map.of.
- [collections.unmodifiableMap](https://docs.oracle.com/javase/10/docs/api/java/util/Collections.html#unmodifiableMap(java.util.Map), 以及其他collections 的不可修改视图方法
Summary
The key design principle here is immutability: using immutable objects and unreassignable variables as much as possible. Let’s review how immutability helps with the main goals of this course:
-
Safe from bugs. Immutable objects aren’t susceptible to bugs caused by aliasing. Unreassignable variables always point to the same object.
-
Easy to understand. Because an immutable object or unreassignable variable always means the same thing, it’s simpler for a reader of the code to reason about — they don’t have to trace through all the code to find all the places where the object or variable might be changed, because it can’t be changed.
-
Ready for change. If an object or a variable can’t be changed at runtime, then code that depends on that object or variable won’t have to be revised when the program changes.
关于mutable对象带来的更多危害,可见下面的原文:
reference