16. Abstract Classes and Methods

Part of CS:2820 Object Oriented Software Development Notes, Spring 2017
by Douglas W. Jones
THE UNIVERSITY OF IOWA Department of Computer Science

 

Where were we?

We've reduced class Intersection to this:

class Intersection { 
        final String name; 
        private LinkedList  outgoing = new LinkedList  ();
        private LinkedList  incoming = new LinkedList  ();
        // Bug: multiple types of intersections -- stoplight, 4-way etc
        // Bug: do I ever need to know about incoming roads?
        
        // initializer
        private Intersection() {}
        
        // other methods
        public String toString() {
                return (
                        "Intersection " +
                        name
                );
        }
}

Despite declaring the initializer to be private, there is still the possibility that code added to class Intersection could later be used to create instances of this class. We can prevent this by declaring the class to be abstract:

abstract class Intersection { 
        final String name; 
        private LinkedList  outgoing = new LinkedList  ();
        private LinkedList  incoming = new LinkedList  ();
        // Bug: multiple types of intersections -- stoplight, 4-way etc
        // Bug: do I ever need to know about incoming roads?
        
        // other methods
        public String toString() {
                return (
                        "Intersection " +
                        name
                );
        }
}

Because the class is abstract, it cannot have an initializer. It is illegal to create an instance of an abstract class. The only way to use an abstract class is to create an instance of a subclass.

It is perfectly legal to include methods in an abstract class. These methods are automatically part of any subclass of the abstract class. The toString() method shown above is an example, but this method is overridden in both subclasses, so we can dispense with it. Or can we?

If we simply delete toString() from class Intersection, the result is that if we have a variable s of class StopLight, we can call s.toString(). However, if we now say Intersection i = s, we have a problem. The assignment i = s is legal because class StopLight is a subclass of class Intersection. Once we make this assignment, however, i.toString() is not legal, or at least, not useful. This is because class Intersection does not have a declared toString() method.

(Actually, it does, since every Java class has a toString method. The problem is, the default method, inherited from class Object, is not particularly useful.)

We can force subclasses of an abstract class to provide new definitions of a method by declaring that method to be abstract.

abstract class Intersection { 
        final String name; 
        private LinkedList  outgoing = new LinkedList  ();
        private LinkedList  incoming = new LinkedList  ();
        // Bug: multiple types of intersections -- stoplight, 4-way etc
        // Bug: do I ever need to know about incoming roads?
        
        // other methods
        public abstract String toString();
}

When you declare an abstract method, you may not provide a body, but you must provide exactly the same parameter list as is used in every subclass that declares an implementation of this method. It is worth noting that a call to a concrete method of a class is fast, while a call to an abstract method requires a little bit of computation. It is only a little bit, though, so you should not let that extra cost concern you.