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.
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
Car
s and Bus
es.
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
.
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!
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.
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.
An abstract class can have abstract methods:
public abstract void drive();
As a client, the abstract
modifier has
no effect on your code.
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.
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.
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.