What are immutable classes?
Immutable classes are the one whose objects visible attributes can not be changed once instantiated.
How to make class immutable?
To make a class immutable adhere followings :
1. Make class final
2. Make all fields final
3. Class should not have any Mutator method ( setter methods)
4. Don't leek any references of class attributes. (For getter of references create new object i.e. deep cloning, return new instance of referenced object.
What are the benefits of immutable?
1. Can be used as Cache:
As immutable objects don't change their state or values its safest cache that can be used across without any danger of corruption.
2. Thread safety by Immutability:
Immutable objects are inherently thread safe and can be shared across multiple threads safely.
3. Can be used for implementing Flyweight Design Pattern:
The Flyweight pattern employs a factory method to dispense references to immutable fine-grained objects and uses sharing to keep the object count down by only having a single instance of the object instead of keep creating instance of these objects through out application. For example String.
4. Good keys for hashing:
Immutable objects are best way to define keys for any hashing data structure. They are memory leek free otherwise due to mutation keys can make associated values not reachable and cause memory leek. In fact we should always use immutable keys.
For example, consider a Person
bean with an value-based equals
method:
Map<Person, String> map = ...
Person p = new Person();
map.put(p, "Hey, there!");
p.setName("Daniel");
map.get(p); // => null
The Person
instance gets "lost" in the map when used as a key because it's hashCode
and equality were based upon mutable values. Those values changed outside the map and all of the hashing became obsolete.
Why Immutable class needs to be final?
For safety and security it is good practice to make class final when it is suppose to be immutable. It's not mandatory but avoids any hack or programming errors. Otherwise in extended sub classes or using anonymous class instances you can override getters and simulate mutability by returning changed values.
For example:
ImmutableClass object = new ImmutableClass(x, y){
@Override
public String getX(){
return "Hello";
}
}
Example of Immutable class:
class User implements Cloneable
{
private String userName;
private int userNode;
User(String name, int node)
{
userName = name;
userNode = node;
}
public String userName()
{
return userName;
}
public int getUserNode() {
return userNode;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public User deepClone() {
return new User(this.userName(), this.getUserNode());
}
@Override
public String toString() {
return String.format("User{userName='%s', userNode=%d}", userName, userNode);
}
}
final class DiskDriveInfo implements Cloneable
{
private final int driveSize;
private final String volumeLabel;
private final User driveShare;
DiskDriveInfo(int size, String volLabel, User share)
{
driveSize = size;
volumeLabel = volLabel;
driveShare = share;
}
public int size()
{
return driveSize;
}
public String label()
{
return volumeLabel;
}
public User getUser() {
// Dont use default clone method as it just creates a new reference to the same object so you will leek reference here.
//return (User) driveShare.clone();
return driveShare.deepClone();
}
// This is just to show that you can write clone method but for immutable objects it has no use.
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return String.format("DiskDriveInfo{driveSize=%d, volumeLabel='%s', driveShare=%s}", driveSize, volumeLabel, driveShare);
}
}