50. Passing/returning mutable objects to/from an immutable class

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!

Before choosing the cloning technique/tool, in certain cases, it is advisable to take your time and analyze/learn different possibilities available in Java and third-party libraries (for example, check the Cloning objects section in this chapter). For shallow copies, the preceding technique can be the proper choice, but for deep copies, the code may need to rely on different approaches such as copy constructor, the Cloneable interface, or external libraries (for example, Apache Commons Lang ObjectUtils, JSON serialization with Gson or Jackson, or any others).
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset