Wiki Markup |
---|
Programmers sometimes incorrectly believe that declaring a field or variable {{final}} makes the referenced object immutable. Declaring variables that have a primitive type to be {{final}} does prevent changes to their values after initialization (other than through the use of the unsupported {{sun.misc.Unsafe}} class). However, when the variable has a reference type, the presence of a {{final}} clause in the declaration only makes _the reference itself_ immutable;. theThe {{final}} clause lackshas anyno effect whatsoever on the referenced object. Consequently, the fields of the referenced object can be mutable. For example, according to the Java Language Specification \[[JLS 2005|AA. Bibliography#JLS 05]\], [Section 4.12.4|http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.12.4], "{{final}} Variables": |
...
Similarly, a final
method parameter obtains an immutable copy of the object reference; once again, this lacks any has no effect on the mutability of the referenced data.
...
Code Block |
---|
|
class Point {
private int x;
private int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
void set_xy(int x, int y) {
this.x = x;
this.y = y;
}
void print_xy() {
System.out.println("the value x is: " + this.x);
System.out.println("the value y is: " + this.y);
}
}
public class PointCaller {
public static void main(String[] args) {
final Point point = new Point(1, 2);
point.print_xy();
// change the value of x, y
point.set_xy(5, 6);
point.print_xy();
}
}
|
...
When the values of the x
and y
members must remain immutable after their initialization, they should be declared final
. However, this obviates the need for the setter method invalidates a set_xy()
method, as it can no longer change the values of x
and y
.
Code Block |
---|
|
class Point {
private final int x;
private final int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
void print_xy() {
System.out.println("the value x is: " + this.x);
System.out.println("the value y is: " + this.y);
}
// set_xy(int x, int y) no longer possible
}
|
With this modification, the values of the instance fields become immutable and consequently match the programmer's intended usage model.
...
Code Block |
---|
|
final public class Point implements Cloneable {
private int x;
private int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
void set_xy(int x, int y) {
this.x = x;
this.y = y;
}
void print_xy() {
System.out.println("the value x is: "+ this.x);
System.out.println("the value y is: "+ this.y);
}
public Point clone() throws CloneNotSupportedException{
Point cloned = (Point) super.clone();
// No need to clone x and y as they are primitives
return cloned;
}
}
public class PointCaller {
public static void main(String[] args) throws CloneNotSupportedException {
final Point point = new Point(1, 2);
point.print_xy();
// Get the copy of original object
Point pointCopy = point.clone();
// pointCopy now holds a unique reference to the newly cloned Point instance
// Change the value of x,y of the copy.
pointCopy.set_xy(5, 6);
// Original value remains unchanged
point.print_xy();
}
}
|
...
Code Block |
---|
|
public static final String[] items = {/* ... */};
|
Clients can trivially modify the contents of the array, even though declaring the array reference to be final
prevents modification of the reference itself.
Compliant Solution (Index Getter)
This noncompliant code example makes the array private
, and provides a public
methods to get individual items and array size
Code Block |
---|
|
private static final String[] items = {/* ... */};
public static final String getItem(int index) {
return items[index];
}
public static final int getItemCount() {
return items.length;
}
|
Compliant Solution (Clone the Array)
...
Code Block |
---|
|
private static final String[] items = {/* ... */};
public static final String[] somethingsgetItems() {
return items.clone();
}
|
...