Java Cloning

 

Cloning in Java is a bit more complicated than we have suggested in the main text. Java defines an interface called Cloneable that a class must implement in order for its objects to be cloned. We have done this behind the scenes in our ur_Robot class, so that all robot classes are Cloneable.

The clone method of Java doesn't return robots, actually. In its declaration it returns things of type Object. Object is the supertype of all Java classes, including robot classes, and when you override the clone method you cannot change this return type. Therefore the declaration of clone in class Object is really (something like)

Object clone()
{	...
}

When we write such a method in a new class (we have done so in ur_Robot) overriding this, we need to keep the same declaration, but we can, of course return any object that is from any subclass of Object according to the usual rules of polymorphism. However, the Java processing system (the compiler) still only knows the return type of this function as Object so it will complain if we say (as we indicated in the main text)

neighbor = clone()

within the NeighborTalker class. This is because neighbor is of class BeeperPutter, not object. We can assign a more specific robot to a robot reference, but not a more general one. Therefore, we need to assure the compiler that we know (even though it does not) that the return type is really a BeeperPutter (in fact it is a NeighborTalker, which is even more specific). So we need to say instead:

neighbor = (BeeperPutter)clone();

Putting the name of a robot class (or any class) before an expression tells the compiler that we believe that the expression has the indicated type. It is called a "cast". In this case we assure the compiler that the result of clone is a BeeperPutter or some subclass of it by casting the result of calling clone to BeeperPutter.

Then a true Java version of setup would be as follows (with only one change).

	public void setup() // PRE: facing West and front is clear
	{	move();
		if (frontIsClear())
		{	neighbor = (BeeperPutter)clone();
		}
		else
		{	neighbor = new NoNeighbor(1, 1, North, 1);
		} 
		goBack();
		neighbor.setup(); 
	} // POST: facing North on original corner.


When we cast as we have done here, the compiler inserts a check to be sure we are correct in our reasoning. If we are wrong, the running program will result in an intent error (a ClassCastException in Java) and we will be informed of it if it occurs.

Notice that a cast doesn't change the type of anything. It just assures the compiler that a value has a more specific type based on the logic of the program than can be deduced from reading the text of the program itself. Its correctness depends on how the program executes.