Tim High (who happens to be the lead architect at the company I work on) correctly points out the relationship between immutable classes and the Flyweight [GoF] pattern. This pattern is another example of when immutable classes should be used. Because a Flyweight object can be potentially shared by many reference objects, flyweight classes should always be immutable.
Attribute sharing
We know that immutable classes help reduce the memory usage because they allow instances to be shared freely. But immutable classes can also share their attributes between instances of the same class, further reducing their memory footprint. String does exactly that:
String a = new String("Hello world");
String b = new String(a);
In the code above, both b and a point to the same array of characters. The constructor that takes another String takes care of not making an unnecessary copy of the array.
Note: Both lines above are bad Java idiom. You should do instead:
String a = "Hello world";
String b = a;
Consider using static factory methods
If you have a limited domain of possible values for your immutable class, you should consider creating static factory methods that return instances from a pool. One classic example is the java.lang.Integer class, that provides the valueOf(int i) method to be used instead of the constructor. Here is the implementation of this method:
public static Integer valueOf(int i) {
final int offset = 128;
if (i >= -128 && i <= 127) {
return IntegerCache.cache[i + offset];
}
return new Integer(i);
}
This method returns a a reference from a pool if the value of the argument is between -128 and 127. You should not only use this method whenever possible, and the equivalents in the other Java wrapper classes, but you should also consider creating similar ones for your own immutable classes.
One final note on the value of immutable classes
One reason why people end up choosing not to make their classes immutable is because the benefits of immutability are less tangible then the benefits of mutability.
If you make your class mutable, you often can 'take a shortcut' in situations where immutability would have added some overhead. If String was mutable, you would never have to create a StringBuffer to do String concatenation in a loop. And the designers of the String class wouldn't have had to 'waste' time writing the StringBuffer class in the first place! But think of the thousands of bugs that would have shown up everywhere when some object inadvertently changed a String that was shared with another object!
So this is a small price to pay for benefits that are not so immediate, which are the elimination of nasty bugs caused by increased coupling, and the reduced memory usage by increased sharing.



0 comments:
Post a Comment