Sponsored Link •
|
Advertisement
|
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.
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 }
int
, float
, ...
ComplexNumber
, a Matrix
,
or a Bitmap
?
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
int
s) 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.
Define objects whose state can't change after they are created.
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.
Here's a step by step outline of this idiom's solution to this problem. In general, whenever you define a Java class:
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?.
String
s 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()); } }
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
|