This is a three part series that takes an indepth look at the who, whats, hows, whens, and whys of using Immutable classes. This article assumes that the author can read/understand/use Java, and has some experience with object oriented development, and maybe design patterns.
What are they?
As their name describes, Immutable objects are objects that cannot be changed after creation. Once the object is created the internal properties of the object are resistant to modification.Typically the signature of an immutable object is: lack of setter methods, private constructors, the avoidance of shallow copies, and preference of deep copies during the creation or in the accessor methods (more about this later).
Why would you use them
Immutable objects, when coming from a “Java Bean” perspective, seems a tad frustrating. With Java beans one can easily modify a bean when needed and pass the result to another component. However with immutable objects, this is not possible. To modify an immutable object, after creation, a copy constructor, or a new object has to be created. This now creates 2 instances of similar, but not entirely equal, objects in memory. The creation of a new object takes up more memory for this new instance. If an object is guaranteed not to have been modified, or modifiable then you, the developer, can trust the data within the object not to be changed while you are using it. The primary benefit of this aspect is extremely useful for concurrency [threading]. Now, that you have an immutable object, there is no longer a need to lock the object just to read its value.
What problems do they solve
Immutable objects don’t solve a very visible problem. They won’t make your program perform new operations. The concept is more of a way of alleviating problems that are potentially in the code already, can enhance readability, provide more security and increase performance. Since the object is assumed to be unchanged, code used to check for modifications [typically referred to dirty bits] is not needed. Additionally lock mechanisms would be eliminated [for reading the object, and technically writing to the object (since that’s not possible with an immutable object)].
Immutable objects are inheritantly more secure; objects are guaranteed to be unmodified. With a modifiable object changes to the object must be validated when they are changed, and after the object has been changed, as opposed to immutable objects [where validation really only occurs at the creation]. Additionally, they are more secure because the creation of the object is the responsibility of another component, how this works will be discussed later.
Immutable objects can become performance enhancement. Since they do not change, the individual instances of the objects can be reused amongst different threads, processes [potentially], and applications without concern of modification of the program’s operation. Additionally with removing the need for locking surrounding the immutable object you can get rid of potential deadlocks and locking overhead. However, with the creation of new objects, one must be aware about the total amount of objects, and memory being allocated. Large amounts of allocations may provoke garbage collection, or worse, cause the application to run out of memory.
The Memory Argument
With exceptions to extreme cases, worrying about the memory usage [compared to using modifiable instances] is more of a sign of a premature optimization, than a reasonable concern. Some of the extreme cases are: very small embedded systems [where available in KBs rather than MB/GBs], or prevalence in your application. If immutable objects are used for the sake of being used, then the benefit of the immutable objects are negated. A few examples include: short life of the objects [the objects are being used only in a short scope and were never written to anyways], needless copying of similar immutable objects (but with a different instance). These abuses of the immutable object pattern can be corrected by using a flyweight pattern, and expiration, to create, cache, and prune the objects. The issue with small systems requires more thought, testing, benchmarking, and prior design before considering immutable objects.
One of the best benefits of immutable objects is that the creation of the object can be managed, and restricted. The creation of the immutable objects can be restricted to factory methods [within a flywheel]. This means that a caching model can distribute previously created objects, and create new requested objects on demand. By using the factory method and fly weight patterns, memory can be efficiently managed and objects can be better managed. Additionally, if the caching module usages WeakReferences, and there are not other usages of the object, then the unused objects would become a higher priority for the garbage collector to collect.
Additionally by introducing hashing, and reusing objects, you are able to get the benefit of having less object allocations in the VM. This, in turns, reduces the probability of the GC running, and needless objects from being created.