Aliasing
Aliasing means that more than one reference is tied to the same object, as in the preceding example. The problem with aliasing occurs when someone writes to that object. If the owners of the other references arent expecting that object to change, theyll be surprised. This can be demonstrated with a simple example:
//: appendixa:Alias1.java
// Aliasing two references to one object.
import com.bruceeckel.simpletest.*;
public class Alias1 {
private static Test monitor = new Test();
private int i;
public Alias1(int ii) { i = ii; }
public static void main(String[] args) {
Alias1 x = new Alias1(7);
Alias1 y = x; // Assign the reference
System.out.println("x: " + x.i);
System.out.println("y: " + y.i);
System.out.println("Incrementing x");
x.i++;
System.out.println("x: " + x.i);
System.out.println("y: " + y.i);
monitor.expect(new String[] {
"x: 7",
"y: 7",
"Incrementing x",
"x: 8",
"y: 8"
});
}
} ///:~
In the line:
Alias1 y = x; // Assign the reference
a new Alias1 reference is created, but instead of being assigned to a fresh object created with new, its assigned to an existing reference. So the contents of reference x, which is the address of the object x is pointing to, is assigned to y, and thus both x and y are attached to the same object. So when xs i is incremented in the statement:
x.i++;
ys i will be affected as well. This can be seen in the output:
x: 7
y: 7
Incrementing x
x: 8
y: 8
One good solution in this case is simply not to do it; dont consciously alias more than one reference to an object at the same scope. Your code will be much easier to understand and debug. However, when youre passing a reference in as an argumentwhich is the way Java is supposed to workyou automatically alias, because the local reference thats created can modify the outside object (the object that was created outside the scope of the method). Heres an example:
//: appendixa:Alias2.java
// Method calls implicitly alias their arguments.
import com.bruceeckel.simpletest.*;
public class Alias2 {
private static Test monitor = new Test();
private int i;
public Alias2(int ii) { i = ii; }
public static void f(Alias2 reference) { reference.i++; }
public static void main(String[] args) {
Alias2 x = new Alias2(7);
System.out.println("x: " + x.i);
System.out.println("Calling f(x)");
f(x);
System.out.println("x: " + x.i);
monitor.expect(new String[] {
"x: 7",
"Calling f(x)",
"x: 8"
});
}
} ///:~
The method is changing its argument, the outside object. When this kind of situation arises, you must decide whether it makes sense, whether the user expects it, and whether its going to cause problems.
In general, you call a method in order to produce a return value and/or a change of state in the object that the method is called for. Its much less common to call a method in order to manipulate its arguments; this is referred to as calling a method for its side effects. Thus, when you create a method that modifies its arguments, the user must be clearly instructed and warned about the use of that method and its potential surprises. Because of the confusion and pitfalls, its much better to avoid changing the argument.
If you need to modify an argument during a method call and you dont intend to modify the outside argument, then you should protect that argument by making a copy inside your method. Thats the subject of much of this appendix.