slanted W3C logo

Day 27 — Abstract Classes and Interfaces

Java has two mechanisms for defining a type that has multiple different implementations.

The first mechanism is abstract classes. An abstract class serves as the superclass for the multiple implementations (the subclasses). The subclasses inherit from (extend) the abstract superclass.

The second mechanism is interfaces. Interfaces are different from inheritance. An interface defines the methods that a class must implement.

Both abstract classes and interfaces allow a client to write code based on a single API.

Abstract Classes

You can use inheritance to gather the common parts (attributes and methods) of two or more classes into a single class. Consider Figure 9.7 from the textbook:


In this example, Vehicle is the superclass of two classes Car and Bus. Vehicle defines all of the attributes and methods that are common to Cars and Buses.

Recognizing Abstract Classes

You can tell if a class is abstract from its API:

public abstract class Vehicle

An abstract class is usually incomplete in that it can declare what methods must exist (ie. drive) but it cannot implement the methods because the methods depend on details only the subclasses will know. Because abstract classes are usually incomplete, Java will not allow you to create an instance of an abstract class. It is a compile-time error to try to create a new Vehicle.

Using Abstract Classes

Although you cannot create an instance of an abstract class, you can create a instance of one of its subclasses (unless the subclass is also abstract). For example, you might be able to create a Car:

Vehicle v = new Car();

Now, you can use v to call any Vehicle method. If Vehicle is well designed for its intended purpose, you may be able to write your entire program using only the Vehicle API!

Using Abstract Classes

Another common way to create objects is to use another class that knows how to create subclass objects; such a class is typically called a factory:

Vehicle v = VehicleFactory.createVehicle("car");
Vehicle w = VehicleFactory.createVehicle("bus");
Vehicle x = VehicleFactory.createVehicle("scooter");

The factory class typically has a static method that knows how to create subclass objects. Such methods are called static factory methods.

Using Abstract Classes

The textbook uses a different example of a static factory method; it puts the method in the Vehicle class:

Vehicle v = Vehicle.createCar();

In this case, Vehicle is both an abstract base class and a factory.

Using Abstract Classes

An abstract class can have abstract methods:

public abstract void drive();

As a client, the abstract modifier has no effect on your code.

Interfaces

An interface is a group of one or more methods that a class promises to implement. For example, objects that have a natural order (numbers, strings, dates, etc) usually promise to implement the Comparable interface:

public interface Comparable
{
   int compareTo(T other);
}

The Comparable interface says that any class that implements the interface must provide a method named compareTo that returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.

An interface is not a class; you cannot create an instance of an interface. Instead, you must find a class that implements the interface and create an instance of that class.

Classes that Implement Interfaces

You can tell if a class implements an interface based on its API:

public final class String
extends Object
implements Serializable, Comparable, CharSequence

Notice that unlike inheritance, a class may implement multiple interfaces.

Even though Comparable is not a class, it is still a valid type; thus, a String is-a Comparable. You can even create a reference to a Comparable object:

public class Interface
{
   public static void main(String[] args)
   {
      // compiles with a warning message
      
      Comparable s = "hello";
      System.out.println(s.compareTo("goodbye"));
   }
}

This is an odd example, but you will see many more examples in Chapter 10.

Classes that Implement Interfaces

You have already seen the Iterable interface; most collection-like classes implement this interface.

public interface Iterable
{
   Iterator iterator();
}

The Iterable interface is what allows you to write for-each loops:

// gc is a GlobalCredit

for (CreditCard cc : gc)
{
   output.println(cc.getNumber());
}


// pf is a Portfolio

for (Investment inv : pf)
{
   output.println(inv.getStock());
}


// stu is a Student

for (String course : stu)
{
   output.println(stu.getCourseGrade(course));
}

Fun fact: Iterator is itself an interface.