Passing mutable objects to an immutable class can break down immutability. Let's consider the following mutable class:
public class Radius {
private int start;
private int end;
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public int getEnd() {
return end;
}
public void setEnd(int end) {
this.end = end;
}
}
Then, let's pass an instance of this class to an immutable class named, Point. At first glance, the Point class can be written as follows:
public final class Point {
private final double x;
private final double y;
private final Radius radius;
public Point(double x, double y, Radius radius) {
this.x = x;
this.y = y;
this.radius = radius;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public Radius getRadius() {
return radius;
}
}
Is this class still immutable? The answer is—no. The Point class is not immutable anymore because its state can be changed as in the following example:
Radius r = new Radius();
r.setStart(0);
r.setEnd(120);
Point p = new Point(1.23, 4.12, r);
System.out.println("Radius start: " + p.getRadius().getStart()); // 0
r.setStart(5);
System.out.println("Radius start: " + p.getRadius().getStart()); // 5
Notice that calling p.getRadius().getStart() returned two different results; therefore, the state of p has been changed, so Point is no longer immutable. A solution to this problem is cloning the Radius object and storing the clone as the field of Point:
public final class Point {
private final double x;
private final double y;
private final Radius radius;
public Point(double x, double y, Radius radius) {
this.x = x;
this.y = y;
Radius clone = new Radius();
clone.setStart(radius.getStart());
clone.setEnd(radius.getEnd());
this.radius = clone;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public Radius getRadius() {
return radius;
}
}
This time, the Point class immutability level has increased (calling r.setStart(5) will not affect the radius field since this field is a clone of r). But the Point class is not completely immutable because there is one more problem to solve—returning mutable objects from an immutable class can break down immutability. Check the following code that breaks down the immutability of Point:
Radius r = new Radius();
r.setStart(0);
r.setEnd(120);
Point p = new Point(1.23, 4.12, r);
System.out.println("Radius start: " + p.getRadius().getStart()); // 0
p.getRadius().setStart(5);
System.out.println("Radius start: " + p.getRadius().getStart()); // 5
Again, calling p.getRadius().getStart() returned two different results; therefore, the state of p has been changed. The solution consists of modifying the getRadius() method to return a clone of the radius field, as follows:
...
public Radius getRadius() {
Radius clone = new Radius();
clone.setStart(this.radius.getStart());
clone.setEnd(this.radius.getEnd());
return clone;
}
...
Now, the Point class is immutable again. Problem solved!