How can we maintain Immutability of a class with a mutable reference
Well, the concept is reading the JLS and understanding it. Chapter 17 of the JLS "Threads and Locks" describes memory visibility and synchronization. Section 17.5 "Final Field Semantics" describes the memory visibility semantics for final fields. That section says in part:
final fields also allow programmers to implement thread-safe immutable objects without synchronization. A thread-safe immutable object is seen as immutable by all threads, even if a data race is used to pass references to the immutable object between threads. This can provide safety guarantees against misuse of an immutable class by incorrect or malicious code. final fields must be used correctly to provide a guarantee of immutability.
The usage model for final fields is a simple one: Set the final fields for an object in that object's constructor; and do not write a reference to the object being constructed in a place where another thread can see it before the object's constructor is finished. If this is followed, then when the object is seen by another thread, that thread will always see the correctly constructed version of that object's final fields. It will also see versions of any object or array referenced by those final fields that are at least as up-to-date as the final fields are.
So you need to:
- Make
address
both final and private. - For any mutable object, you must prevent the reference to that object from being seen externally.
In this case, #2 probably means you can't return a reference to Address like you have with getAddress()
. And you have to make a defensive copy in the constructor. I.e., make a copy of any mutable parameter, and store the copy in Employee. If you can't make a defensive copy, there's really no way to make Employee immutable.
public final class Employee{
private final int id;
private final Address address;
public Employee(int id, Address address)
{
this.id = id;
this.address=new Address(); // defensive copy
this.address.setStreet( address.getStreet() );
}
public int getId(){
return id;
}
public Address getAddress() {
Address nuAdd = new Address(); // must copy here too
nuAdd.setStreet( address.getStreet() );
return nuAdd;
}
Implementing clone()
or something similar (a copy ctor) would make creating defensive objects easier for complicated classes. However, the best recommendation I think would be to make Address
immutable. Once you do that you can freely pass around its reference without any thread-safety issues.
In this example, notice I do NOT have to copy the value of street
. Street
is a String, and strings are immutable. If street
consisted of mutable fields (integer street number for example) then I would have to make a copy of street
also, and so on ad infinitum. This is why immutable objects are so valuable, they break the "infinite copy" chain.
Since this question is getting popular, I should also add a mention of Brian Goetz's book, Java Concurrency in Practice, which is how I learned about these techniques, and I'm basically paraphrasing that book above.
In Employee class, return the deep clone copy of the address instance in the getAddress() method instead of returning original instance. So that if anybody changed the address instance, it can not reflect the original instance. But one condition, Address class must implements Cloneable interface.
public final class Employee{
private final int id;
private Address address;
public Employee(int id, Address address) {
this.id = id;
this.address=address;
}
public int getId(){
return id;
}
public Address getAddress() throws CloneNotSupportedException{
return (Address) address.clone();
}
}
Reference :-Immutable Class in java
Well there is steps provided by Java docs
A Strategy for Defining Immutable Objects
The following rules define a simple strategy for creating immutable objects. Not all classes documented as "immutable" follow these rules. This does not necessarily mean the creators of these classes were sloppy — they may have good reason for believing that instances of their classes never change after construction. However, such strategies require sophisticated analysis and are not for beginners.
- Don't provide "setter" methods — methods that modify fields or objects referred to by fields.
- Make all fields final and private.
- Don't allow subclasses to override methods. The simplest way to do this is to declare the class as final. A more sophisticated approach is to make the constructor private and construct instances in factory methods.
- If the instance fields include references to mutable objects, don't allow those objects to be changed:
- Don't provide methods that modify the mutable objects.
- Don't share references to the mutable objects. Never store references to external, mutable objects passed to the constructor; if necessary, create copies, and store references to the copies. Similarly, create copies of your internal mutable objects when necessary to avoid returning the originals in your methods.
Address class is mutable because you can modify it with the setStreet method. So other class can modify this class.
We can defend against this by taking a copy of the of the Address instance when it is passed in rather than trusting the reference to the instance we are given.
Making Address object final
private final Address address;
Secondly,
this.address = new Address(address.getStreet());
Create constructor in Address class that sets Street.Remove setter method for street.
And finally instead of
public Address getAddress(){
return address;
}
Use
public Address getAddress(){
return new Address(address.getStreet());
}