第15条:使可变性最小化
通过一个复数类来看不可变类。
public final class Complex { private final double re; private final double im; private Complex(double re, double im) { this.re = re; this.im = im; } public static Complex valueOf(double re, double im) { return new Complex(re, im); } public static Complex valueOfPolar(double r, double theta) { return new Complex(r * Math.cos(theta), r * Math.sin(theta)); } public static final Complex ZERO = new Complex(0, 0); public static final Complex ONE = new Complex(1, 0); public static final Complex I = new Complex(0, 1); public double realPart() { return re; } public double imaginaryPart() { return im; } public Complex add(Complex c) { return new Complex(re + c.re, im + c.im); } public Complex subtract(Complex c) { return new Complex(re - c.re, im - c.im); } public Complex multiply(Complex c) { return new Complex(re * c.re - im * c.im, re * c.im + im * c.re); } public Complex divide(Complex c) { double tmp = c.re * c.re + c.im * c.im; return new Complex((re * c.re + im * c.im) / tmp, (im * c.re - re * c.im) / tmp); } @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Complex)) return false; Complex c = (Complex) o; return Double.compare(re, c.re) == 0 && Double.compare(im, c.im) == 0; } @Override public int hashCode() { int result = 17 + hashDouble(re); result = 31 * result + hashDouble(im); return result; } private int hashDouble(double val) { long longBits = Double.doubleToLongBits(re); return (int) (longBits ^ (longBits >>> 32)); } @Override public String toString() { return "(" + re + " + " + im + "i)"; } }
复数类具有实部和虚部,它提供的加减乘除运算都是返回新的Complex实例,而不是修改这个实例。不可变对象有很多优点,它只有一种状态,即被创建时的状态,而且前面也提到过,它本质上是线程安全的。
这里还用到一个小技巧,对频繁使用的值,为它们提供公有的静态final常量。
还有一种构建不可变类的常用方法,让类的构造器私有化,并添加上公有静态工厂方法,前面也提到过。
String类就是不可变类一个典型例子。但是细心的读者肯定会发现String类的hash属性不是final类型的,事实上,许多不可变类都拥有一个或多个非final的域,在第一次请求的时候计算出来缓存在这些域中,从而节约了计算所需要的花销。因为对象是不可变的,它的不可变性保证了计算结果相同。
public final class String{ private final char value[]; private int hash; public int hashCode() { int h = hash; //只有hash不存在时才去计算 if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; } }
第16条:复合优先于继承
简而言之,只有当两者确实存在is-a的关系时,才使用继承。否则,都应该使用复合。对于这一条,应用最典型的例子就是设计模式中的装饰者模式。