代码改变世界

Effective Java 15 Minimize mutability

2014-03-10 17:24  小郝(Kaibo Hao)  阅读(675)  评论(0编辑  收藏  举报

Use immutable classes as much as possible instead of mutable classes.

Advantage

  1. Easy to design, implement and use than mutable classes.
  2. Less prone to error and more secure.
  3. Immutable objects are simple.
  4. Immutable objects are inherently thread-safe; they require no synchronization.
  5. Immutable objects and their internals can be shared freely between threads.
  6. Immutable calss can provide static factories that cache frequently requested instances to avoid creating new instances.
  7. Immutable objects make great building blocks for other objects.(eg. Use immutable object as the key of map or set colletion)  

Disadvantage

Immutable classes require a separate object for each distinct value which may be costly.  

Principles:

  1. Don't provide any methods that modify the object's state.
  2. Ensure that the class can't be extended.
  3. Make all fields final.
  4. Make all fields private.
Ensure exclusive access to any mutable components.  

/**

* Example code for Minimize mutability

*/

package com.effectivejava.classinterface;

   

/**

* @author Kaibo

*

*/

public final class Complex {

private final double re;

private final double im;

   

public Complex(double re, double im) {

this.re = re;

this.im = im;

}

   

// Accessors with no corresponding mutators

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;

// See page 43 to find out why we use compare instead of ==

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)";

}

   

/**

* @param args

*/

public static void main(String[] args) {

Complex c1 = new Complex(1.0, 2.0);

Complex c2 = new Complex(1.0, 2.0);

Complex c3 = new Complex(3.0,4.0);

   

System.out.printf("c1.equals(c2) = %s%n", c1.equals(c2));

System.out.printf("c1.equals(c3) = %s%n",c1.equals(c3));

System.out.printf("c1 + c2 = %s%n", c1.add(c2));

System.out.printf("c1 - c2 = %s%n", c1.subtract(c2));

System.out.printf("c1 * c2 = %s%n", c1.multiply(c2));

System.out.printf("c1 / c2 = %s%n", c1.divide(c2));

}

}  

Note:

You can use alternative immutable implementation to not permit class to be subclassed instead of use final decorate to the class. Just use the static factory method and private constructor to constrain this which enable the client outside the package to use this class freely and providing the extensibility for caching.  

// Immutable class with static factories instead of constructors

public 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));

}

   

... // Remainder unchanged

}  

BigInteger and BigDecimal are not final

If you write a class whose security depends on the immutability of a BigInteger or BigDecimal argument from an untrusted client, you must check to see that the argument is a "real" BigInteger or BigDecimal, rather than an instance of an untrusted subclass. If it is the latter, you must defensively copy it under the assumption that it might be mutable (Item 39)

public static BigInteger safeInstance(BigInteger val) {

if (val.getClass() != BigInteger.class)

return new BigInteger(val.toByteArray());

return val;

}  

Serializability.

If you choose to have your immutable class implement Serializable and it contains one or more fields that refer to mutable objects, you must provide an explicit readObject or readResolve method, or use the ObjectOutputStream.writeUnshared and ObjectInputStream.readUnsharedmethods, even if the default serialized form is acceptable. Otherwise an attacker could create a mutable instance of your not quite-immutable class. This topic is covered in detail in Item76.  

Summary

If a class cannot be made immutable, limit its mutability as much as possible. make every field final unless there is a compelling reason to make it non-final.