abstract classes and polymorphism

 

Introduction

Some classes are designed to be extended but NOT instantiated.

Consider, for example, a dog, a cat, a whale, and a human. Each is a mammal. In object-oriented design, Mammal would be the superclass and Dog, Cat, Whale, and Human would be classes that each extend Mammal. But, since there is no such thing as a generic mammal, there is no reason to instantiate a Mammal object.

The Mammal class is an example of a class that should be declared abstract.

 

An abstract class

public abstract class Mammal

which indicates that an object of the class can never be instantiated. The class can, however, be extended by a subclass that would inherit all its features (to be covered shortly). For example,

public class Cat extends Mammal

would be the header of a Cat class that extends the Mammal class.

public abstract double someMethod(double parm1, int parm2);

declares an abstract method named someMethod that requires two parameters (a double and an int) and returns a double value to the caller.

The purpose of an abstract method is to force certain features to exist within a subclass. An extending class must either override the abstract method to define the method body or declare itself to be abstract. If it does not override the abstract method or declare itself to be abstract, a compile error will occur.

public class App {
  public static void main(String[] args) {
    Cat myCat = new Cat("Fluffy", 10);
    System.out.println(myCat.toString());
  }
}

abstract class Mammal {
  private String name;
  public Mammal(String iName) {
    setName(iName);
  }
  public boolean setName(String nName) {
    if (nName.length() > 0) {
      name = nName;
      return true;
    }
    else {
      name = "unknown";
      return false;
    }
  }
  public String getName() {
    return name;
  }
  public abstract boolean setAge(int nAge);
  public abstract int getAge();
  public abstract String toString();
}

class Cat extends Mammal {
  private int age;
  public static final int MAX_LIFE = 25;
  public Cat(String iName, int iAge) {
    super(iName);
    setAge(iAge);
  }
  public boolean setAge(int nAge) {
    if (nAge >= 0 && nAge <= MAX_LIFE) {
      age = nAge;
      return true;
    }
    else
      return false;
  }
  public int getAge() {
    return age;
  }
  public String toString() {
    return getName() + " (cat), " + age;
  }
}

Notes:

  1. The Mammal class encapsulates a mammal's name and supports the "setting" and "getting" of its name. The first two abstract methods will force subclasses to implement methods to "set" and "get" the mammal's age. The third abstract method will force subclasses to implement a meaningful toString() method.

  2. The Cat class subclasses Mammal to encapsulate information about a cat's age. It defines the setAge() and getAge() methods to store and retrieve the age of the cat. It also defines the toString() method to return a meaningful String of information about the cat.

 

Polymorphism

- Webster's Collegiate Dictionary

Mammal x = new Cat("Fluffy", 10);
System.out.println(x.toString());

This works because the object being constructed is a Cat and a Cat is a Mammal. Regardless of how we reference the object, however, it will always remain a Cat and have the features of a Cat.

Because an object reference can reference different kinds of objects, we can write some powerful code. For example, assume that Cat, Mouse, Sheep, and Wolf are subclasses of Mammal with each having its own implementation of the toString() method. If someMammal is a Mammal object reference that currently references either a Cat, a Mouse, a Sheep, or a Wolf, the expression

someMammal.toString()

will call the toString() method of the correct class. The object reference, someMammal, is polymorphic because it has the ability to take on different forms. It will "act like" an object of the class it currently references.

public class App {
  public static void main(String[] args) {

    // Declare an array of superclass object references.

    Mammal[] pets = new Mammal[5];

    // Load the array with objects that descend from the superclass.

    pets[0] =
new Cat("Fluffy", 10);
    pets[1] =
new Mouse("Speedy", 1);
    pets[2] =
new Mouse("Mickey", 2);
    pets[3] =
new Cat("Tiger", 3);
    pets[4] =
new Cat("Garfield", 15);

    // Use polymorphism to display the contents of the array.

    Utility.skip();
    for (int i = 0; i < pets.length; i++) {
      System.out.println("\t" +
pets[i].toString());
    }
  }
}

abstract class Mammal {
  private String name;
  public Mammal(String iName) {
    setName(iName);
  }
  public boolean setName(String nName) {
    if (nName.length() > 0) {
      name = nName;
      return true;
    }
    else {
      name = "unknown";
      return false;
    }
  }
  public String getName() {
    return name;
  }
  public abstract boolean setAge(int nAge);
  public abstract int getAge();
  public abstract String toString();
}

class Cat extends Mammal {
  private int age;
  public static final int MAX_LIFE = 25;
  public Cat(String iName, int iAge) {
    super(iName);
    setAge(iAge);
  }
  public boolean setAge(int nAge) {
    if (nAge >= 0 && nAge <= MAX_LIFE) {
      age = nAge;
      return true;
    }
    else
      return false;
  }
  public int getAge() {
    return age;
  }
  public String toString() {
    return getName() + " (cat), " + age;
  }
}

class Mouse extends Mammal {
  private int age;
  public static final int MAX_LIFE = 5;
  public Mouse(String iName, int iAge) {
    super(iName);
    setAge(iAge);
  }
  public boolean setAge(int nAge) {
    if (nAge >= 0 && nAge <= MAX_LIFE) {
      age = nAge;
      return true;
    }
    else
      return false;
  }
  public int getAge() {
    return age;
  }
  public String toString() {
    return this.getName() + " (mouse), " + age;
  }
}

 

More information

Java ranch has an excellent "campfire story" relating to this lesson. Be sure to read How my Dog learned Polymorphism.

 

Looking ahead

When using polymorphism, it is sometimes necessary to determine the class of an object being referenced. It is also sometimes necessary to convert (cast) one object as another. These topics, and more, will be covered in the next lesson.

 

Lab exercise for Ferris students

E-mail your answers to this assignment no later than the due date listed in the class schedule.

 

Review questions

  1. True or False: An abstract class must contain at least one abstract method.

  1. True

  2. False 

  1. True or False: An abstract class can have one or more constructors, instance variables, and instance methods.

  1. True

  2. False

  1. What will happen when an attempt is made to compile and execute the following program? Line numbers are for reference purposes only.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class App {
  public static void main(String[] args) {
    Base x = new Sub();
    System.out.println(x.toString());
    x.hello();
  }
}
abstract class Base {
  public String toString() {
    return "I am a Base";
  }
  public abstract void hello();
}
class Sub extends Base {
  public String toString() {
    return "I am a Sub";
  }
}
  1. A compile error will occur at line 3

  2. A compile error will occur at line 14

  3. Compiles successfully but a runtime error will occur at line 5

  4. Compiles and runs to display:  "I am a Base"

  5. Compiles and runs to display:  "I am a Sub"

  1. What will happen when an attempt is made to compile and execute the following program? Line numbers are for reference purposes only.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class App {
  public static void main(String[] args) {
    Base[] subs = new Base[2];
    subs[0] = new Sub();
    subs[1] = new SubSub();
    subs[1].hello();
  }
}
abstract class Base {
  public abstract void hello();
}
class Sub extends Base {
  public void hello() {
    System.out.println("I am a Sub");
  }
}
class SubSub extends Sub {
  public void hello() {
    System.out.println("I am a SubSub");
  }
}
  1. A compile error will occur at line 3

  2. A compile error will occur at line 4

  3. Compiles successfully but a runtime error will occur at line 5

  4. Compiles and runs to display:  "I am a Sub"

  5. Compiles and runs to display:  "I am a SubSub"