这节主要是讲 可变对象给编程带来的危害,所谓不可变对象,就是整个生命周期中不可变的对象(废话), e.g. : String
具体来说参见 Basic Java when we discussed snapshot diagrams
看以下两段代码:
/** @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));
}
main
方法的第一印象应该是sumAbs()
是求绝对值,而sum
是求和,而这导致了一个非常深的bugSafe 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
修饰先看下面一段代码:
/** @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
就被抛出了。
至少有两种解决方案可解决这个问题:
date
而是用imutable 的替代物: package java.time: LocalDateTime, Instant,groudAns
的副本return new Date(groundhogAnswer.getTime());
不过第二种方法虽然解决的问题,可是在大多数情况下我们都只需要一个共享的 groudAns
因此会带来一些性能上的问题。
重点说一些这个可变对象多出引用的问题,如果可变对象仅在一个局部被一个变量引用,那么用可变对象肯定是没有什么危害的,可是大多数情况下,可变对象被多出引用,这样就导致了可变对象引入的bug
Date
s, use the appropriate immutable type from java.time based on the granularity of timekeeping you need.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