The Artima Developer Community
Sponsored Link

Guideline 6
Interface Design by Bill Venners
Use Immutables to represent values of abstract data types

Advertisement

Interface Design | Contents | Previous | Next

With a mutable object like the StampDispenser in Listing 3-1, identity is important. If I have 3 stamp dispenser machines and I enter a dime in one and a dime in another, I don't get a stamp. I have to insert at least 20 cents in a particular stamp dispenser to get the stamp. The identity of the machine into which I insert that second dime matters -- it has to be the same machine in which I inserted the first dime.

The Immutable

  1 package com.artima.examples.complexnum.ex1;
  2
  3 /**
  4 * Represents a complex number whose real and imaginary
  5 * components are <CODE>double</CODE>s.
  6 */
  7 public class ComplexNumber {
  8
  9     private double real;
 10     private double imaginary;
 11
 12     /**
 13     * Construct a new <code>ComplexNumber</code> with the passed
 14     * real and imaginary components.
 15     */
 16     public ComplexNumber(double real, double imaginary) {
 17
 18         this.real = real;
 19         this.imaginary = imaginary;
 20     }
 21
 22     /**
 23     * Returns the real component of this <code>ComplexNumber</code>.
 24     */
 25     public double getReal() {
 26         return real;
 27     }
 28
 29     /**
 30     * Returns the imaginary component of this <code>ComplexNumber</code>.
 31     */
 32     public double getImaginary() {
 33         return real;
 34     }
 35
 36     /**
 37     * Returns the conjugate of this complex number. The conjugate
 38     * of complex number a + <em>i</em>b is a - <em>i</em>b.
 39     */
 40     public ComplexNumber getConjugate() {
 41
 42         return new ComplexNumber(real, -imaginary);
 43     }
 44
 45     /**
 46     * Compares the passed <CODE>ComplexNumber</CODE> to this
 47     * <code>ComplexNumber</code> for equality. Two <code>ComplexNumber</code>
 48     * objects are semantically equal if their real components are
 49     * equal and their imaginary components are equal.
 50     *
 51     * @param An object to compare to this <code>ComplexNumber</code>
 52     * @return <code>true</code> if this <code>ComplexNumber</code> is semantically equal
 53     *    to the passed <code>ComplexNumber</code>
 54     */
 55     public boolean equals(Object o) {
 56
 57         if ((o == null) || (getClass() != o.getClass())) {
 58             return false;
 59         }
 60
 61         ComplexNumber cn = (ComplexNumber) o;
 62
 63         // Because this class extends Object, don't
 64         // call super.equals()
 65
 66         // To be semantically equal, the complex numbers
 67         // must have equal real and imaginary components
 68         if ((real != cn.real) || (imaginary != cn.imaginary)) {
 69             return false;
 70         }
 71
 72         return true;
 73     }
 74
 75     /**
 76     * Computes the hash code for this <code>ComplexNumber</code>.
 77     *
 78     * @return a hashcode value for this <code>ComplexNumber</code>
 79     */
 80     public int hashcode() {
 81
 82         // Got this idea from Double's hashcode method. 
 83         long re = Double.doubleToLongBits(real);
 84         long im = Double.doubleToLongBits(imaginary);
 85
 86         int rex = (int) (re ^ (re >>> 32));
 87         int imx = (int) (im ^ (im >>> 32));
 88
 89         return rex ^ imx;
 90     }
 91
 92     /**
 93     * Adds the passed <code>ComplexNumber</code> to this one. The sum of
 94     * two complex numbers a + <em>i</em>b and c + <em>i</em>d is
 95     * (a + c) + <em>i</em>(c + d).
 96     *
 97     * @param addend the <code>ComplexNumber</code> to add to this one
 98     * @returns the sum of this and the passed <code>ComplexNumber</code>
 99     */
100     public ComplexNumber add(ComplexNumber addend) {
101         return new ComplexNumber(real + addend.real, imaginary + addend.imaginary);
102     }
103
104     /**
105     * Subtracts the passed <code>ComplexNumber</code> from this one. The difference
106     * between two complex numbers, a + <em>i</em>b - c + <em>i</em>d, is
107     * (a - c) + <em>i</em>(b - d).
108     *
109     * @param subtrahend the <code>ComplexNumber</code> to subtract from this one
110     * @returns the difference of this and the passed <code>ComplexNumber</code>s
111     */
112     public ComplexNumber sub(ComplexNumber subtrahend) {
113         return new ComplexNumber(real - subtrahend.real, imaginary - subtrahend.imaginary);
114     }
115
116     /**
117     * Multiplies this <code>ComplexNumber</code> (the multiplicand) with
118     * the passed <code>ComplexNumber</code> (multiplier). The product of two
119     * complex numbers a + <em>i</em>b and c + <em>i</em>d is
120     * (ac - bd) + <em>i</em>(bc + ad).
121     *
122     * @returns the difference of this and the passed <code>ComplexNumber</code>s
123     */
124     public ComplexNumber mult(ComplexNumber multiplier) {
125
126         double prodReal = (real * multiplier.real) - (imaginary * multiplier.imaginary);
127         double prodImaginary = (imaginary * multiplier.real) + (real * multiplier.imaginary);
128
129         return new ComplexNumber(prodReal, prodImaginary);
130     }
131
132     /**
133     * Divides this <code>ComplexNumber</code> (the numerator) by
134     * the passed <code>ComplexNumber</code> (denominator). The quotient of two
135     * complex numbers a + <em>i</em>b / c + <em>i</em>d is
136     * ((ac + bd) + <em>i</em>(bc - ad)) / (c*c + d*d).
137     *
138     * @returns the difference of this and the passed <code>ComplexNumber</code>s
139     */
140     public ComplexNumber div(ComplexNumber denominator) {
141
142         // Perform division by first multiplying both the numerator and denominator
143         // by the conjugate of the denominator.
144         ComplexNumber conj = denominator.getConjugate();
145         ComplexNumber tempNumerator = mult(conj);
146         ComplexNumber tempDenominator = denominator.mult(conj);
147
148         // By multiplying the denominator by its conjugate, its imaginary
149         // component drops to zero. Can then just divide the real and imaginary
150         // components of the numerator by the denominator's real value.
151         return new ComplexNumber(tempNumerator.real / tempDenominator.real,
152             tempNumerator.imaginary / tempDenominator.real);
153     }
154 }

When to Use Immutables

The main difference between the State Machine and the immutable is the way the object reacts to messages being sent (via methods invoked on the public interface). Whereas the State Machine changes its own state, the Immutable creates a new object of its own class that has the new state and returns it.

Another way to think about objects is as Abstract Data Types. For example, you may want a ComplexNumber object, or a Matrix object. You may want a BigInteger object. You may want a Color object or a String, or a BitMap object. Each of these concepts is a relatively simple thing. All classes represent data types, one way to look at object-oriented programming is that by defining classes you are extending the programming language with new types. Java comes with int, float, and so on built-in (plus the classes in java.lang, and you are adding new ones.

You want to restrict immutables for objects that are small, because a lot of them may get created. On the other hand, because they don't have the aliasing problems associated with State Machines, they don't have to be cloned when passed to other methods. In other words, they can be shared. This could actually result in fewer objects being created.

Messenger objects are usually immutable. Actor objects are by definition immutable, because if you don't have state, you can't change your state. But ADT type immutables actually could be implemented as State Machines, because they offer services that would ordinarily be though of as something that would change their state. But instead of changing state, they create a new object of their class that has the new state and return that.

Make objects immutable where appropriate. (Exceptions, most Events, things that will need to be passed around (like String, make a StringBuffer too.)) Perhaps talk about ways to pass objects to methods when you want to make sure the method doesn't alter the object. Use an immutable object especially if the object is small or represents a fundamental data type

RGBColor # 3: Thread safety through immutability
Here's an immutable version of RGBColor:

// In file threads/ex3/RGBColor.java
// Instances of this immutable class
// are thread-safe.
public class RGBColor {

    private final int r;
    private final int g;
    private final int b;

    public RGBColor(int r, int g, int b) {

        checkRGBVals(r, g, b);

        this.r = r;
        this.g = g;
        this.b = b;
    }

    /**
    * returns color in an array of three ints: R, G, and B
    */
    public int[] getColor() {

        int[] retVal = new int[3];
        retVal[0] = r;
        retVal[1] = g;
        retVal[2] = b;

        return retVal;
    }

    public RGBColor invert() {

        RGBColor retVal = new RGBColor(255 - r,
            255 - g, 255 - b);

        return retVal;
    }

    private static void checkRGBVals(int r, int g, int b) {

        if (r < 0 || r > 255 || g < 0 || g > 255 ||
            b < 0 || b > 255) {

            throw new IllegalArgumentException();
        }
    }
}

Note that the setColor() method is simply removed, as it doesn't make sense in an immutable RGBColor object. The getColor() method, which reads the instance variables, is identical to what it has been, except now it doesn't have to be synchronized. The invert() method, which writes to the instance variables, is changed. Instead of inverting the current object's color, this new invert() creates a new RGBColor object that represents the inverse of the object upon which invert() is invoked, and returns a reference to that object.

Using immutable objects
Achieving thread safety by making objects immutable (Approach 2) works well when objects are small and represent values of a simple abstract data type. The Java API includes several examples of immutable objects, including String and the primitive type wrappers such as Integer, Long, Float, Boolean, Character, and so on.

It's worth noting that instances of the AWT's Color class are immutable. Likewise, the immutable approach may make sense for this article's RGBColor class, which is similar in functionality to the AWT's Color class, because RGBColor objects are small (they contain only 3 ints) and conceptually represent values of a simple abstract data type.

Another benefit of immutable objects is that you can pass references to them to methods without worrying that the method will change the object's state. In addition, if the overhead of immutability (excessive creation of short-lived objects) may at times be too inefficient, you can also define a mutable companion class that can be used when the immutable version isn't appropriate. An example of this design approach in the Java API is the StringBuffer class, which serves as a mutable companion to the immutable String class. Note that the StringBuffer class is also thread-safe, but it uses the "normal" approach: its instance variables are private and its critical sections are synchronized.

Intent

Define objects whose state can't change after they are created.

Also Known As

Motivation

Immutable objects have several different uses. They are thread-safe. They can't be changed when passed to methods. They act more like values of abstract data types than state machines.

Recipe

Here's a step by step outline of this idiom's solution to this problem. In general, whenever you define a Java class:

  1. Make sure each constructor is able to create a complete object.
  2. Don't provide any non-private methods that change the state.
  3. Instead of transforming data, have methods create and return a new instance of the type.

Note that defining finalize() is not part of this idiom. A finalizer is not appropriate in the general case. Cases in which it is appropriate are described in item?.

Example Code

Here's some Java code that illustrates using the I Shall Return idiom to return multiple Strings from a method that parses a name.
// A more interesting one might be a matrix.
public class ComplexNumber
    implements Cloneable, Serializable {

    private double real;
    private double imaginary;

    public ComplexNumber(double real, double imaginary) {
        this.real = real;
        this.imaginary = imaginary;
    }

    public ComplexNumber add(ComplexNumber cn) {
        return new ComplexNumber(real + cn.real,
            imaginary + cn.imaginary);
    }

    public ComplexNumber sub(ComplexNumber cn) {
        return new ComplexNumber(real - cn.real,
            imaginary - cn.imaginary);
    }

    public ComplexNumber mult(ComplexNumber cn) {
        return new ComplexNumber(real * cn.real,
            imaginary * cn.imaginary);
    }

    public ComplexNumber div(ComplexNumber cn) {
        return new ComplexNumber(real / cn.real,
            imaginary / cn.imaginary);
    }
}

class Test {

    public static void main(String[] args) {

        NameManager.SplitNameReturnVal sn =
            NameManager.splitName("F. Scott Fitzgerald");

        System.out.println(sn.getFirst() + " " + sn.getMiddle()
            + " " + sn.getLast());
    }
}

Implementation guidelines

Variants

Also can define a mutable companion class, such as StringBuffer is to String. Also think about File objects that can be closed, but not opened again. This is a one time only kind of object, not quite immutable, but not reusable. This kind of object also appears often and this kind of object can kind of be passed down but not really.

Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use