Inheritance and Polymorphism
Overview
Teaching: 60 min
Exercises: 60 minQuestions
How do we use inheritance and polymorphism in Java?
Objectives
Explain the inheritance constructs and apply them in shapes.
Build on the example Rectangle class with subclasses and interfaces.
Polymorphism and inheritance
- Create three rectangles in the main() method as follows.
Rectangle rect2 = new Rectangle(10.0,5.0);
Rectangle rect3 = new Rectangle(10.0,5.0);
Rectangle rect4 = rect3;
Then use println() to print the three rectangle’s ‘value’.
System.out.println("rect2: " + rect2);
System.out.println("rect3: " + rect3);
System.out.println("rect4: " + rect4);
When run the program, you will see the output like this: Rectangle@7852e922, which is the class name together with the hashcode of the object in hexadecimal (you may understand it as the memory address for each object). You may see the hashcode of rect2 and rect3 are different, because whenever you new an object, the compiler will allocate a new block of memory for it. While you must have seen that rect3 and rect4 have the same hash code, because these two objects point to the same memory address. This means that whenever you change any attribute’s value for one object, the other object’s attribute value also changes. In another word, you may treat them as one object, but with two names (rect3 and rect4). Try the following statements yourself.
rect3.zoom(0.5);
System.out.println("rect3’s width: " + rect3.getWidth());
System.out.println("rect4’s width: " + rect4.getWidth());
Re-run it, you must see both rect3 and rect4 have been zoomed into half size.
toString() method
Every well-designed Java class should contain a public method called tostring() that returns a short description of the instance (in a return type of String). The toString() method can be called explicitly (via objectName.toString()) just like any other method; or implicitly through println() or print(). If an instance is passed to the println(objectName) method, the toString() method of that instance will be invoked implicitly. Now, add the following toString() method to the Rectangle class:
// Return a description of a rectangle in the form of
// Rectangle[x=*,y=*,w=*,h=*]
public String toString(){
return
"Rectangle[x="+originX+",y="+originY+",w="+width+",h="+height+"]";
}
Re-compile and re-run RectangleApp.java, you must have seen the outputs of the following statements are different.
System.out.println("rect2: " + rect2);
System.out.println("rect3: " + rect3);
System.out.println("rect4: " + rect4);
This is because if a class doesn’t have the toString() method, the compiler will output the default ”className@hashcode”1
. If a class has defined the toString() method, the compiler will output the form which you customize. As metioned above, you may call toString() method explicitly, just like any other methods:
System.out.println("rect2: " + rect2.toString());
Inheritance
You will have noticed that much of the code in Circle.java and Rectangle.java are duplicated. This is poor practice as it can lead to inconsistencies in the code through duplication.
- Refactor your code to create a class called Shape.java that contains all the common features of Circle.java and Rectangle.java. Consider if this should be a class, an asbtract class or an interface. Speak to the workshop leader if you are unsure which option to go for.
- Create an interface called Movable that includes one method public void move(double x, double y).
- Create a subclass that extends Rectangle called Wall.java.
- Create a subclass from Rectangle called Paddle.java that implements Movable. Remember: you will need to add the class specific constructors.
- Create a subclass from Circle called Ball.java that implements Movable.
- For each Movable class add additional class members to handle direction.
- Overload the overlaps method to overlaps(Shape s). Calculate the overlap for the relevant input class using instanceof.
Visualising the objects
- Add an abstract method to Shape called ‘public void paint(Graphics g)’
- Override this method for Rectangles with the code below:
public void paint(Graphics g) { g.setColor(Color.RED); g.fillRect(x, y, width, height); // Fill a rectangle }
- Override this method for Circle with the following:
public void paint(Graphics g) { g.setColor(color); g.fillOval(x, y, radius, radius); // Fill a circle }
- Override this method for Rectangles with the code below:
- Create the following class to create a canvas with a paint method: `import java.awt.*; // Using AWT’s Graphics and Color
public class CanvasGUI extends Canvas { public void paint(Graphics g) { Rectangle r1 = new Rectangle(0, 400,20,100); r1.paint(g); }`
- Create a ShapeApp.java class with the code below:
public static void main(String[] args) { JFrame frame = new JFrame("My Drawing"); Canvas canvas = new CanvasGUI(); canvas.setSize(400, 400); frame.add(canvas); frame.pack(); frame.setVisible(true); }
Key Points
Object-oriented programming is a programming paradigm that uses abstraction (in the form of classes and objects) to create models based on the real world environment