An immutable
class is simply a class whose instances cannot be modified. All of the
information contained in each instance is provided when it is created and is
fixed for the lifetime of the object. The Java platform libraries contain many
immutable classes, including String, the boxed
primitive classes, and BigInteger and BigDecimal. There are many good reasons for this:
Immutable classes are easier to design, implement, and use than mutable
classes. They are less prone
to error and
are more secure.
To make a
class immutable, follow these five rules:
- Don’t provide any methods that modify the
object’s state (known as mutators).
- Ensure that the class can’t be extended. This
prevents careless or malicious subclasses from compromising the immutable
behavior of the class by behaving as if the object’s state has changed.
Preventing subclassing is generally accomplished by making the class
final, but there is an alternative that we’ll discuss later.
- Make all fields final. This
clearly expresses your intent in a manner that is enforced by the system.
Also, it is necessary to ensure correct behavior if a reference to a newly
created instance is passed from one thread to another without
synchronization, as spelled out in the memory
model [JLS, 17.5; Goetz06 16].
- Make all fields private. This
prevents clients from obtaining access to mutable objects referred to by
fields and modifying these objects directly. While it is technically
permissible for immutable classes to have public final fields containing
primitive values or references to immutable objects, it is not recommended
because it precludes changing the internal representation in a later
release (Item 13).
5. Ensure exclusive
access to any mutable components. If your class has any fields that refer to
mutable objects, ensure that clients of the class cannot obtain references to
these objects. Never initialize such a field to a client-provided object
reference or return the object reference from an accessor. Make defensive copies (Item 39) in constructors, accessors, and readObject methods (Item 76).
Here is a
slightly more complex example:
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;
// 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)";
}
}
Immutable
objects are simple.
Immutable
objects are inherently thread-safe; they require no synchronization.
Immutable
objects can be shared freely.
One easy way
to do this is to provide public static final constants for frequently used
values. For example, the Complex class might
provide these constants:
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);
This approach
can be taken one step further. An immutable class can provide static factories
(Item 1) that cache frequently requested instances to avoid creating new
instances when existing ones would do. All the boxed primitive classes and BigInteger do this.
A consequence
of the fact that immutable objects can be shared freely is that you never have
to make defensive copies (Item 39). In fact, you never
have to make any copies at all because the copies would be forever equivalent
to the originals. Therefore, you need not and should not provide a clone method or copy
constructor (Item 11) on an immutable class. This was not well understood in the early days of the Java
platform, so the String class does
have a copy constructor, but it should rarely, if ever, be used (Item 5).
Not only can you
share immutable objects, but you can share their internals.
Immutable
objects make great building blocks for other objects, whether
mutable or immutable.
The only real
disadvantage of immutable classes is that they require a separate object for
each distinct value.
To make this
concrete, here’s how Complex would look if
you took this approach:
// 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);
}
... // Remainder unchanged
}
While this
approach is not commonly used, it is often the best alternative. It is the most
flexible because it allows the use of multiple package-private implementation
classes. To its clients that reside outside its package, the immutable class is
effectively final because it is impossible to extend a class that comes from
another package and that lacks a public or protected constructor. Besides
allowing the flexibility of multiple implementation classes, this approach
makes it possible to tune the performance of the class in subsequent releases
by improving the object-caching capabilities of the static factories.
Classes should
be immutable unless there’s a very good reason to make them mutable. Immutable
classes provide many advantages, and their only disadvantage is the potential
for performance problems under certain circumstances. You should always make
small value objects, such as PhoneNumber and Complex, immutable. (There are several classes in
the Java platform libraries, such as java.util.Date
and
java.awt.Point, that should
have been immutable but
aren’t.) You
should seriously consider making larger value objects, such as String and BigInteger, immutable as
well.
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 nonfinal.
A final note
should be added concerning the Complex class in this
item. This example was meant only to illustrate immutability. It is not an
industrial-strength complex number implementation.
Reference: Effective Java 2nd Edition by Joshua Bloch