11.9. Reusing a Class Via InheritanceMany children have a hard time with the concepts of left and right. Even some adults get these confused. Let's create a new class ConfusedTurtle that will turn right when asked to turn left and will turn left when asked to turn right. How can we do this? In Section 11.2.2 we saw that you can specify the parent class when you declare a class using the extends keyword. If you don't specify the parent class the parent class will be Object. So to create a class ConfusedTurtle that is a child of the Turtle class do the following: public class ConfusedTurtle extends Turtle { } Because the class ConfusedTurtle inherits from the class Turtle it inherits all its fields and methods. We want to override the method turnLeft() and have it turn right instead. How can we do this? We could just have it call the method turnRight() but we are overriding this method as well. We really want to call the method in the parent class Turtle. Java gives us a way to do this using the keyword super. Usually the Java Virtual Machine (JVM) will start looking for a method in the class that created the current object. But if we use the keyword super to invoke a method, we will start looking for the method in the parent class of the class that created the current object. public class ConfusedTurtle extends Turtle { /** * Method to turn right (but a confused * turtle will actually turn left) */ public void turnRight() { super.turnLeft(); } If we try to compile this we will get an error. The problem is that we haven't created any constructors yet. The compiler will try to add the no-argument constructor for us. But since ConfusedTurtle inherits from Turtle it will also add a call to super() which is a call to the parent's no-argument constructor. It does this to allow initialization of the inherited fields. But, the Turtle class doesn't have a no-argument constructor. Remember that we always passed a World object when we created a new Turtle object (Section 3.4.2). If we check the Turtle class we don't see a constructor that takes a World object. But there is a constructor that takes a ModelDisplay object. The World class implements the ModelDisplay interface so we can call this constructor and pass in a World object. We need to add a constructor to the ConfusedTurtle class that takes a ModelDisplay object. Then, the first thing we will do is call the parent constructor that takes a ModelDisplay object. To do this we use super(modelDisplayObj). A call to a superclass (parent) constructor must be the first line of code in a constructor. If the compiler doesn't find a call to the parent constructor as the first line of code in a child constructor, it will add a call to the parent's no-argument constructor. public class ConfusedTurtle extends Turtle { //////////////// constructors /////////////////////// /** * A constructor that takes a ModelDisplay object * @param modelDisplayObj the thing that does the display */ public ConfusedTurtle(ModelDisplay modelDisplayObj) { // use parent constructor super(modelDisplayObj); } //////////////// methods /////////////////////////// /** * Method to turn right (but a confused * turtle will actually turn left) */ public void turnRight() { super.turnLeft(); } To try this out we can do the following: > World world = new World(); > ConfusedTurtle fred = new ConfusedTurtle(world); > fred.forward(); > fred.turnLeft(); > fred.forward(); > fred.turnRight(); > fred.forward(); Notice that the ConfusedTurtle object still knows how to go forward. The only difference between it and a Turtle object is what happens when it is asked to turn left or right, as shown in Figure 11.17. Figure 11.17. Result of commands to a confused turtle.What happens if we also override the turn method in the ConfusedTurtle class to actually turn (360 - the passed degrees)? /** * Method to turn by the passed degrees * (a confused turtle will turn by 360- the * passed degrees) */ public void turn(int degrees) { super.turn(360-degrees); } We can try this out in the interactions pane with the following: > World world = new World(); > ConfusedTurtle fred = new ConfusedTurtle(world); > fred.turn(90); > fred.forward(); > fred.turnLeft(); > fred.forward(); > fred.turnRight(); > fred.forward(); If we try this out in the interactions pane we will see that the turn method is doing the right thing but the turnLeft() and turnRight() methods are not (Figure 11.18). What happened? We have to remember that the Java Virtual Machine will always start looking for a method in the class that created the current object (unless we use super.method()). Figure 11.18. Result of overriding the turn method.When we call turnLeft() on a ConfusedTurtle object it will invoke the method in the ConfusedTurtle class (Figure 11.19). In that method we call super.turnRight(). This will start looking for the turnRight() method in the Turtle class but not find such a method. Then it will look in the parent class of Turtle, which is SimpleTurtle. The turnRight() method in SimpleTurtle contains this.turn(90); (Figure 11.19). Figure 11.19. Diagram of the methods executed by fred.turnLeft(). |