Read-only classes
Although the local copy produced by clone( ) gives the desired results in the appropriate cases, it is an example of forcing the programmer (the author of the method) to be responsible for preventing the ill effects of aliasing. What if you’re making a library that’s so general purpose and commonly used that you cannot make the assumption that it will always be cloned in the proper places? Or more likely, what if you want to allow aliasing for efficiency—to prevent the needless duplication of objects—but you don’t want the negative side effects of aliasing?
One solution is to create immutable objects that belong to read-only classes. You can define a class such that no methods in the class cause changes to the internal state of the object. In such a class, aliasing has no impact since you can read only the internal state, so if many pieces of code are reading the same object, there’s no problem.
As a simple example of immutable objects, Java’s standard library contains “wrapper” classes for all the primitive types. You might have already discovered that, if you want to store an int inside a container such as an ArrayList (which takes only Object references), you can wrap your int inside the standard library Integer class:
//: appendixa:ImmutableInteger.java
// The Integer class cannot be changed.
import java.util.*;
public class ImmutableInteger {
public static void main(String[] args) {
List v = new ArrayList();
for(int i = 0; i < 10; i++)
v.add(new Integer(i));
// But how do you change the int inside the Integer?
}
} ///:~
The Integer class (as well as all the primitive “wrapper” classes) implements immutability in a simple fashion: It has no methods that allow you to change the object.
If you do need an object that holds a primitive type that can be modified, you must create it yourself. Fortunately, this is trivial. The following class uses the JavaBeans naming conventions:
//: appendixa:MutableInteger.java
// A changeable wrapper class.
import com.bruceeckel.simpletest.*;
import java.util.*;
class IntValue {
private int n;
public IntValue(int x) { n = x; }
public int getValue() { return n; }
public void setValue(int n) { this.n = n; }
public void increment() { n++; }
public String toString() { return Integer.toString(n); }
}
public class MutableInteger {
private static Test monitor = new Test();
public static void main(String[] args) {
List v = new ArrayList();
for(int i = 0; i < 10; i++)
v.add(new IntValue(i));
System.out.println(v);
for(int i = 0; i < v.size(); i++)
((IntValue)v.get(i)).increment();
System.out.println(v);
monitor.expect(new String[] {
"[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]",
"[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
});
}
} ///:~
IntValue can be even simpler if privacy is not an issue, the default initialization to zero is adequate (then you don’t need the constructor), and you don’t care about printing it out (then you don’t need the toString( )):
class IntValue { int n; }
Fetching the element out and casting it is a bit awkward, but that’s a feature of ArrayList, not of IntValue.