8: Abstract Classes, Polymorphism and Interfaces

What We Will Cover


Illuminations

Questions about the Midterm Exam?

Questions from 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 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 of one class type to be used like another class type.

  • In Java, 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; }
}

8.2: Interfaces

Learner Outcomes

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

  • Declare and Implement interfaces
  • Use interfaces as arguments to methods

8.2.1: Introduction to Interfaces

  • Sometime you want to have subclasses implement certain methods
  • For example, recall our abstract Shape class define an abstract method for this purpose
    public abstract void draw();
  • Then all subclasses of Shape had to implement the draw() method
  • However, sometimes you cannot use inheritance for this because you already have an inheritance hierarchy and classes can inherit from only one superclass
  • The solution for this problem is to use an interface
  • A Java interface defines a set of methods that an object must have to interact with a system
  • In addition, an interface may define static constants
  • In Java, interfaces define and standardize ways that objects interact with each other
  • The next example shows an interface for a programmer-defined system with a method that any object must implement for drawing

Example Interface

  • Here is an example of a simple interface:
    public interface Drawable {
        public static final int FILLED = 0;
        public static final int UNFILLED = 1;
        public void draw();
    }
    
  • Provides two integer constants
  • Declares a single abstract method named draw()
  • Note that the interface does not provide any code for the method
  • An interface can only provide a method signature

Example Interface Implementation

  • A class that implements an interface must:
    1. Declare that it is implementing the interface
      implements Drawable
    2. Provide code for all the methods of the interface
  • Here is an example class implementing the Drawable interface shown above:
    class Shape implements Drawable {
        public void draw() {
            System.out.println("Drawing a Shape.");
        }
    }
    

8.2.2: Abstract Classes vs. Interfaces

  • Abstract classes and interfaces have many similarities and differences
  • Both abstract classes and interfaces allow you to define constants
  • In addition, both abstract classes and interfaces allow you to declare abstract methods
  • These similarities mean that interfaces can sometimes be used instead of an abstract class

Advantages of an Abstract Class

  • An abstract class can have instance variables while an interface cannot
  • An abstract class can define regular methods while interfaces can only declare abstract methods
  • An abstract class can define static methods while an interface cannot

Advantages of an Interface

  • A class can only inherit from one class but can directly implement many interfaces
  • Any object that implements an interface can be used wherever the interface is accepted

When Will You Need Interfaces?

  • You often need to implement interfaces in the Java API
  • In particular, interfaces are used in coding graphical user interfaces
  • You design interfaces most often when creating larger applications

8.2.3: Coding Custom Interfaces

  • Declaring an interface is similar to declaring a class
  • You use the keyword interface instead of class
  • General syntax:
    public interface InterfaceName {
        [public] [static] [final] dataType CONSTANT_NAME = value;
        returnType methodName(parameterList);
    }
    
  • Where:
    • InterfaceName: the name you make up for the interface
    • dataType: the type of constant
    • CONSTANT_NAME: the name you make up for the constant
    • value: the value of the constant
    • returnType: the type of value returned by the method, or void for no return value
    • methodName: the name you make up for the method
    • parameterList: the types and names of the parameters
  • The constant variables and abstract methods are optional
  • For example:
    public interface Drawable {
        public static final int FILLED = 0;
        public static final int UNFILLED = 1;
        public void draw();
    }
    
  • Declares an abstract method named draw()
    • You cannot write the implementation of the method in the interface
    • Instead, you write the implementation of the method in any class that implements the interface
  • All methods are public and abstract by default
  • Similarly, all constants are public static final by default:
    • Using the keywords public static final is optional
    • Though optional, they help to document the scope
  • Note that interface methods cannot be declared static
  • Any interface method that is implemented becomes an instance method

Extending Interfaces

  • You can derive new interfaces from an existing one just like classes can extend classes
  • One difference is that an interface can extend multiple interfaces
  • Syntax:
    public interface InterfaceName
        [extends SuperInterface1 [, SuperInterface2]...] { }
    
  • Where:
    • InterfaceName: the name you make up for the interface
    • SuperInterfaceX: the name of the interface being extended
  • For example, we could split our Drawable interface into two superinterfaces and one subinterface
  • The superinterfaces now contain the constants:
    interface DrawingConstants {
        public static final int FILLED = 0;
    }
    
    interface DrawingConstants2 {
        public static final int UNFILLED = 1;
    }
    
  • The subinterface contains the method declaration:
    interface Drawable
            extends DrawingConstants, DrawingConstants2 {
        public void draw();
    }
    
  • The new Drawable has the same capabilities as the old Drawable

8.2.4: Implementing Interfaces

  • Any class can choose to implement zero or more interfaces
  • Syntax:
    public class ClassName [extends SuperClass]
        [implements Interface1 [, Interface2]...] { }
    
  • Where:
    • ClassName: the name you make up for the class
    • SuperClass: the name of the parent class
    • InterfaceX: the name of the interface being implemented
  • Classes implementing an interface add the keyword implements in the class header
  • After the implements keyword, code a comma-delimited list of interface names
  • For example:
    public class Map implements Drawable
    
  • Class Map must now implement the method draw()
  • Note that you can refer to a constant declared in the interface without the interface name if you implement the interface
    int filled = UNFILLED;
  • A class that implements an interface must implement all methods of the interface:
    public class Map implements Drawable {
        int filled = UNFILLED;
        public void draw() {
            System.out.println("Drawing a Map.");
        }
    }
    

8.2.5: Interfaces as Arguments

  • A method that accepts an interface as an argument can accept any object implementing that interface
  • In many ways, this make an interface more powerful than an abstract class
  • For example, here is a method that accepts the Drawable interface:
    public static void drawTwice(Drawable d) {
        d.draw();
        d.draw();
    }
    
  • We can pass any object to the drawTwice() method that implements the Drawable interface
  • You can see a complete example in the following code

Example Method with an Interface Argument

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
interface Drawable {
    public void draw();
}

class Map implements Drawable {
    public void draw() {
        System.out.println("Drawing a Map.");
    }
}

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

public class ShapeApp {
    public static void main(String[] args) {
        Map m = new Map();
        Shape s = new Shape();
        drawTwice(m);
        drawTwice(s);
    }

    public static void drawTwice(Drawable d) {
        d.draw();
        d.draw();
    }
}
  • Since both the Map and Shape class implements Drawable, they must implement the draw() method
  • We can send either a Map or a Shape to the drawTwice() method
  • Creates a flexible polymorphic design that lets us easily add new classes into the system

8.2.6: Interfaces and Polymorphism

  • You can implement polymorphism using interfaces rather than an abstract class
  • It is also easy to mix both interfaces and abstract classes in one design
  • Here is an example implementation

Example Polymorphic Design Using Interfaces and Abstract Classes

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
interface Drawable {
    void draw();
}

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

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

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 Map implements Drawable {
    public void draw() {
        System.out.println("Drawing a Map.");
    }
}

public class Polymorph {
    Shape[] s = { new Circle(), new Rectangle(),
                 new Square() };           //(1)
    Drawable[] d = { new Shape(), new Rectangle(),
                    new Map() };           //(2)

    public static void main(String args[]) {
        Polymorph poly = new Polymorph();
        poly.drawShapes();
        poly.drawDrawables();
    }

    public void drawShapes() {
        System.out.println("Draw shapes:");
        for (int i = 0; i < s.length; i++)    //(3)
            s[i].draw();
    }

    public void drawDrawables() {
        System.out.println("Draw drawables:");
        for (int i = 0; i < d.length; i++)    //(4)
            d[i].draw();
    }
}
  • At (1), shapes[] holds references to various Shape objects
  • At (2), drawables[] holds references to classes that implement Drawable interface
  • Calling the draw() method at (3) and (4) relies on polymorphism
  • At runtime, dynamic lookup determines which draw() method to use
  • It is easy to add new classes to the Shape or Drawable arrays
  • There is no need to change any conditional logic

8.2.7: Summary

  • An even more abstract "class" is an interface
  • An interface can contain:
    • Static constants
    • Abstract methods
  • For example:
    public interface Drawable {
        public static final int FILLED = 0;
        public static final int UNFILLED = 1;
        public void draw();
    }
    
  • The advantage of an interface is that classes can implement many interfaces
  • Provides many of the advantages of multiple inheritance without the headaches
  • All methods of an interface are public and abstract by default
  • Similarly, all constants are public static final by default
  • You can extend interfaces using the extends keyword:
    public interface InterfaceName
        [extends SuperInterface1 [, SuperInterface2]...] { }
    
  • Any class can choose to implement zero or more interfaces
  • When it does, it must implement all the methods of the interface:
    public class Map implements Drawable {
        public void draw() {
            System.out.println("Drawing a Map.");
        }
    }
    
  • A method that accepts an interface as an argument can accept any object that implements that interface:
    public static void drawTwice(Drawable d) {
        d.draw();
        d.draw();
    }
    
  • You can use interfaces to implement polymorphic systems as well

Exercise 8.2

Take one minute to prepare an answer the following question:

  1. Which of the following statements are true about interfaces:
    1. Members of an interface are never static
    2. Members of an interface can always be defined static
    3. Interfaces can implement variables but not methods
    4. Interfaces can extend any number of other interfaces
    5. Constants in an interface must be declared public or compilation will fail

Wrap Up

Due Next:
Midterm Exam (4/7/10)
A7-Alien Polymorphism (4/14/10)

Home | Blackboard | Schedule | Room Policies | Syllabus
Help | FAQ's | HowTo's | Links
Last Updated: April 05 2010 @12:29:19