Java Reference
In-Depth Information
At this point, the
wrapper
object holds
101
and there is no way to change it. Therefore, the
IntWrapper
class is an
immutable class and its objects are immutable objects. You might have noticed that two changes were made to the
IntHolder
class to convert it to the
IntWrapper
class. The
setValue()
method was removed and the
value
instance
variable was made
final
. In this case, it was not necessary to make the
value
instance variable
final
. The use of the
final
keyword makes your intention clear to the reader of the class and it protects the
value
instance variable from
being changed inadvertently. It is good practice (use it as a rule of thumb) to declare all instance variables that define the
immutable state of an object
final
so the Java compiler will enforce the immutability during compile time. The objects
of
IntWrapper
class are immutable internally as well as externally. There is no way to change its state once it is created.
Let's create a variant of the
IntWrapper
class, which will be externally immutable but internally mutable. Let's call
it
IntWrapper2
. It is listed in Listing 7-17.
Listing 7-17.
An Example of an Externally Immutable and Internally Mutable Class
// IntWrapper2.java
package com.jdojo.object;
public class IntWrapper2 {
private final int value;
private int halfValue = Integer.MAX_VALUE;
public IntWrapper2(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public int getHalfValue() {
// Compute half value if it is not already computed
if (this.halfValue == Integer.MAX_VALUE) {
// Cache the half value for future use
this.halfValue = this.value / 2;
}
return this.halfValue;
}
}
IntWrapper2
adds another instance variable called
halfValue
, which will hold the half value of the value that is
passed to the constructor. It is a trivial example. However, it serves the purpose to explain what you mean by externally
and internally immutable objects. Suppose (just for the sake of this discussion) that computing half of an integer is a
very costly process and you do not want to compute it in the constructor of the
IntWrapper2
class, especially if nobody
every asks for it. The
halfValue
instance variable is initialized to the maximum integer value, which works as a flag
that it is not computed yet. You have added a
getHalfValue()
method, which checks if you have already computed
the half value. For the first time, it will compute the half value and cache it in
halfValue
instance variable. From the
second time onward, it will simply return the cached value.
The question is, “Is an
IntWrapper2
object immutable?” The answer is yes and no. It is internally mutable.
However, it is externally immutable. Once it is created, its client will see the same return value from the
getValue()
and
getHalfValue()
methods. However, its state (
halfValue
to be specific) changes once in its lifetime when the
getHalfValue()
method is called for the first time. However, this change is not visible to the users of the object. This
method returns the same value on all subsequent calls. Objects like
IntWrapper2
are called immutable objects. Recall
that typically an immutable object means externally immutable.