Item 15 最小化类的可变性
effective java
如何做到让类不可变?
- 不提供改变对象状态的方法。(mutators)
- 确保类不会被继承,即用final来限定类。
- 让所有的域(field)为final。
- 让所有的域private。
- 确保所有对可变组件的互斥访问(Ensure exclusive access to any mutable components)
例子:
public final class Complex{
//final class
private final double re;
//private final field
private final double im;
public Complex(double re,double im){
this.re = re;
this.im = im;
}
public double realPart(){return re;}
public double imaginaryPart(){return im;}
public Complex add(Complex c){
return new Complex(re+c.re,im+c.im);
//return new object instead of modify itself
}
//Other arithmetic method ommit...
}
为什么要使用不可变对象(immutable objects)?
- 不可变对象很简单。不需要考虑对象在变化中是否会变得行为怪异。只要在构造方法中有限制,不可变对象就一直是有效的。
不可变对象本质上是线程安全的,不需要同步(synchronization)。不可变对象是鼓励共享而不用担心安全。例如以下这种:
public static final Complex ZERO = new Complex(0,0);
- 不可变对象的内部也是鼓励共享的。
不可变对象作为其他复杂对象的构件(building blocks)是很优秀的。其不变的性质降低了复杂对象的设计难度。
不可变对象的缺点以及优化
- 不可变对象的唯一缺点:每次操作都需要创建新对象,在多步操作中造成性能问题。
- 优化:
- 在多步操作中使用可变的“伴随类”(mutable "companion class")。例如String类与String Buffer类。
- 预测哪些多步操作是经常被使用的,提供这些操作的官方方法进行优化。
最佳实践
- 提供公有的静态工厂方法(static factory method)并让构造器为私有。
- 可以提前检测输入数据的有效性,如果不合法可以直接避免创建对象。
- 可以缓存一些常用的对象,优化性能。
- 可以添加一些实用的功能。
- 制造不可变类的条件可以弱化为任何方法都不能造成对象外部表现的改变。这样我们可以创造一些内部的可变组件用于缓存,进而优化性能。
总结
- 设计一个类时首先考虑是否能让它不可变。
- 如果类不能做成完全不可变的,将其可变性限制到最低。
- 不要在静态工厂方法和构造器之外提供任何的初始化方法。