8: Abstract Classes and Polymorphism

What We Will Cover


Illuminations

Questions about the Midterm Exam?

Questions about the last class?

8.1: Abstract Classes and Polymorphism

Learner Outcomes

At the end of the lesson the student will be able to:

  • Code abstract classes
  • Describe the elements of a polymorphic system
  • Generate polymorphic behavior in Java programs

8.1.1: Abstract Classes and Methods

  • When designing a program using inheritance, you often create a superclass that you do not want instantiated
  • Instead, you only want subclasses of this superclass to be instantiated
  • The superclass contains code common to all the subclasses but should not be instantiated
  • For example: Shape
  • You cannot draw a generic shape -- what would it look like?
  • Instead, you create subclasses that implement specific shapes
  • Here is an example of an inheritance hierarchy for drawing various shapes
Inheritance Hierarchy

Shape hierarchy

Creating Abstract Classes

  • One solution to preventing a superclass from being instantiated is to make it abstract
  • Abstract classes provide common code for all its subclasses
  • A subclass inherits all non-private variables and methods from the abstract class
  • An abstract class often defines a set of operations for subclasses, providing a common interface for the subclasses

Coding an Abstract Class

  • Any class can become abstract using the keyword abstract
  • Declaring a class abstract means that you cannot instantiate an object of the class
  • Instead, you must subclass the abstract superclass to make use of its operations
  • The following example shows a class defined as abstract

Example Abstract Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public abstract class Shape {
    // regular instance variable declarations
    private int x1, x2, y1, y2;

    // regular constructor and method declarations
    public Shape() { }
    public Shape(int newX1, int newY1, int newX2,
            int newY2) {
        x1 = newX1;
        x2 = newX2;
        y1 = newY1;
        y2 = newY2;
    }
    public int getX1() { return x1; }
    public int getX2() { return x2; }
    public int getY1() { return y1; }
    public int getY2() { return y2; }

    // Abstract method declaration
    public abstract void draw();
}

Abstract Methods

  • An abstract class may or may not contain abstract methods
  • An abstract method is a method declaration without a method body:
    public abstract void draw();
    
  • Any abstract methods must be implemented in any non-abstract subclass
  • You must declare a class abstract if it contains an abstract method

8.1.2: Introduction to Polymorphism

  • Any object oriented language must implement three programming mechanisms: Polymorphism, Inheritance and Encapsulation (PIE)
    • We have already covered encapsulation and inheritance
    • Thus, polymorphism is the last piece of the PIE.

    Polymorphism: the ability to redefine methods for subclasses and decide which definition to use at runtime.

  • Polymorphism lets us treat subclasses just like their superclass
  • This is an important ability because it allow us to generalize program code

Polymorphism Example

  • Suppose you are designing a graphics package
  • You have classes for several figures such as lines, rectangles and squares
  • Each figure is an object of a different class
  • In a well-designed program, all of these shapes would be subclasses of one superclass, call it Shape

    shape hierarchy

  • The superclass has code common to all the subclasses
  • For example, it might have variables for x and y coordinates to locate the object on the screen
  • Also, it would have get and set methods for the variables
  • In addition, the Shape class has an abstract methods named draw() that is overridden in each subclass
  • Each subclass only implements the code it needs, such as overriding the draw() method to draw its shape on the screen

The Application Program

  • To finish our design, we need to have a way to draw all the shapes on the screen
  • We make this easy, we decide to store all our shapes in an array named shapes[]
    private Shape shapes[];
  • We can store each object reference to our subclasses in the array
  • For instance:
    shape[x] = new Line()
  • All we have to do now is call the draw() method of each Shape subclass
  • This is easily and elegantly done using a for loop:
    for (Shape s : shapes) {
        s.draw();
    }
    
  • Will this design work?

8.1.3: Implementing Polymorphism

  • It turns out that this design works out well in Java
  • Calling the correct drawing method of the subclass is done automatically
  • Here is a simple implementation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
abstract class Shape {
    private int x1, x2, y1, y2;

    // regular constructor and method declarations
    public Shape() { }
    public Shape(int newX1, int newY1, int newX2,
            int newY2) {
        x1 = newX1;
        x2 = newX2;
        y1 = newY1;
        y2 = newY2;
    }
    public int getX1() { return x1; }
    public int getX2() { return x2; }
    public int getY1() { return y1; }
    public int getY2() { return y2; }

    // Abstract method declaration
    public abstract void draw();
}

class Line extends Shape {
    public void draw() {
        System.out.println("Drawing a Line.");
    }
}

class Rectangle extends Shape {
    public void draw() {
        System.out.println("Drawing a Rectangle.");
    }
}

class Square extends Rectangle {
    public void draw() {
        System.out.println("Drawing a Square.");
    }
}

class Oval extends Shape {
    public void draw() {
        System.out.println("Drawing an Oval.");
    }
}

public class DrawingApp {
    private Shape shapes[] = {new Line(),
        new Rectangle(), new Square(), new Oval()};

    public static void main(String args[]) {
        DrawingApp app = new DrawingApp();
        app.drawShapes();
    }

    public void drawShapes() {
        for (Shape s : shapes) {
            s.draw();
        }
    }
}
  • So why does this work?
  • Answer: dynamic binding

Static vs. Dynamic Binding

  • Binding: the process of associating a method definition with a method call.
  • Static binding: done at compile time
  • Dynamic binding: done at run-time
  • Compilation binds memory locations to methods before running a program
  • Binding done at compile time is called static, or early, binding
  • In contrast, binding done at run time is called dynamic, or late, binding
  • Java uses dynamic binding for all instance methods
  • In contrast, static methods are bound at compile time (static binding)
  • Polymorphism in Java relies on late binding and thus instance methods

8.1.4: Making Changes

  • Let us suppose that our program is already written and in use
  • Now a customer wants us to add a new shape like a triangle
  • To make the change, we create a Triangle class and add it to the program
  • We want to make minimal changes and so have Triangle subclass Shape
  • Do we need to change Shape to implement this new class?

Adding a Triangle

  • Here is the code for our Triangle class:
    class Triangle extends Shape {
        public void draw() {
            System.out.println("Drawing a Triangle.");
        }
    }
    
  • All need now is a way to add Triangle objects to the shapes array
  • For our simple application:
    private Shape shapes[] = { new Line(),
            new Rectangle(), new Square(),
            new Oval(), new Triangle() };
    
  • Note how easy it is to change a polymorphic program
  • One of the chief advantages of polymorphism is extensibility

8.1.5: Summary

  • Abstract classes cannot be instantiated
  • The purpose of an abstract class is to provide common code for all subclasses
  • Abstract classes often implement abstract methods like:
    public abstract void draw();
  • An abstract method requires that non-abstract subclasses implement the method
  • Polymorphism is a feature of inheritance that lets you treat subclasses just like their superclass type
  • Programs are written to use methods declared in the superclass
  • Superclass methods are overridden in subclasses
  • Objects of subclass types are created and stored as superclass references
    private Shape shapes[];
  • The correct method is called automatically using a process known as dynamic binding
    for (Shape s : shapes) {
        s.draw();
    }
    
  • Polymorphism is used to make systems more easily extensible
    • Encourages object decoupling by reducing dependencies
    • Classes are easily added to the system
  • More information: Polymorphism in object-oriented programming

Check Yourself

  1. What is an abstract class?
  2. Why is an abstract class useful in programming?
  3. How do you declare a class to be abstract?
  4. What is an abstract method?
  5. What is the purpose of an abstract method?
  6. What is polymorphism?
  7. What determines which method is run in the following:
    Shape s = new Square();
    s.draw();
    
    1. The type of the object
    2. The type of the reference variable
  8. What is the advantage of a polymorphic system?

Exercise 8.1

Take two minutes to prepare an answer the following questions:

  1. What is the class hierarchy of the following Polymorphism code?
  2. What will be the result of attempting to compile and run the following code?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Polymorphism {
    public static void main(String[] args) {
        A ref1 = new C();
        B ref2 = (B) ref1;
        System.out.println(ref2.f());
    }
}

class A { int f() { return 1; } }
class B extends A {
    int f() { return 2; }
}
class C extends B {
    int f() { return 3; }
}

Wrap Up

Due Next:
Midterm Exam (4/1/09)
A7-Alien Polymorphism (4/8/09)

Home | Blackboard | Announcements | Schedule | Room Policies | Course Info
Help | FAQ's | HowTo's | Links
Last Updated: April 05 2009 @21:53:20