如何使类的可变性最小化?

实现将类变为不可变性

Posted by MasterJen on November 20, 2018

Hey 不变类

The secret of success is constancy to purpose. – 成功的秘诀在于对目标的忠实.

读 Effective Java 之 不可变类.

不可变类只是其实例不能被修改的类,每个实例中包含的信息都必须在创建该实例的时候就提供,并在对象的整个生命周期内固定不变.

为什么使用不可变类呢? 不可变类比可变类更易于设计,实现和使用,它们不易出错,并且更加安全.

我们现在熟知的不可变类有 String ,基本的类型的包装类,BigInteger ,BigDecimal.

想要让类变为不可变的,需要遵循5个原则:

1 不要提供任何会修改对象状态的方法;
2 保证类不会背拓展.
3 是所有的域都是final的.
4 是所有的域都变为私有的.
5 确保对任何可变组建的互斥访问.

举个例子: 实现复数之间的加减乘除操作.

public  final  class Complex {
//    不可变属性
    private final double re;
    private final double im;

    public Complex(double re, double im) {
        this.re = re;
        this.im = im;
    }
//   方法得到 re
    public double realPart(){
        return re;
    }

    public double imaginaryPart(){
        return im;
    }
//  复数的 相减操作  并没有修改之前的,而是创建了新的实例
    public Complex subComple(Complex complex){
        return new Complex(re-complex.re,im-complex.re);
    }
//  复数的相加
    public Complex addComple(Complex complex){
        return new Complex(re+complex.re,im+complex.im);
    }
//  复数的 相乘
    public Complex multiComple(Complex complex){
        return new Complex(re*complex.re-im*complex.im,re*complex.re+im*complex.im);
    }
//  复数的相除
    public Complex divideComple(Complex complex){
        double tmp = re*complex.re+complex.im*im;
        return new Complex((re*complex.re+im*complex.im)/tmp,(im*complex.re-re*complex.im)/tmp);
    }
//   重写了 equals 方法
    @Override
    public boolean equals(Object obj) {
        if(obj == this){
            return true;
        }
        if(!(obj instanceof Complex))
        return false;
        Complex complex = ((Complex) obj);
        return Double.compare(re,complex.re)==0&&Double.compare(im,complex.im)==0;
    }
//  重写了 hashCode

    /**
     * 之所以使用 31, 是因为他是一个奇素数.
     * 如果乘数是偶数,并且乘法溢出的话,信息就会丢失,
     * 因为与2相乘等价于移位运算(低位补0).
     * 使用素数的好处并不很明显,但是习惯上使用素数来计算散列结果.
     * 31 有个很好的性能,即用移位和减法来代替乘法,
     * 可以得到更好的性能: 31 * i == (i << 5) - i,
     * 现代的 VM 可以自动完成这种优化.这个公式可以很简单的推导出来.
     * @return
     */
    @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(val);
        return ((int) (longBits ^ (longBits >>> 32)));
    }

    @Override
    public String toString() {
        return "Complex{" +
                "re=" + re +
                ", im=" + im +
                '}';
    }
}

这样就是类变成了不可变的,不过也有着一些缺点,就是不同的值,都有一个独立的对象,所以,根据自己的需求使用吧.