Class and interface hierarchy

See also:

Specification vs. implementation

Interfaces contain a specification, but no implementation, classes contain a specification and an implementation

A specification is

An implementation is

Black-box tests look at specifications; glass-box tests look at implementation

Extends vs implements

Interfaces extend other interfaces. Classes extend other classes. Classes implement interfaces.

Think of "A extends B" as "an A is a B with some other stuff". For example, if interface I2 extends interface I1, then we start with the methods and requirements in I1, and add the additional methods and requirements given in I2. If class C2 extends class C1, then we start with the fields and methods in C1, and then add the additional fields and methods given in C2.

Think of "C implements I" as a promise that objects of class C meet the specifications given in I.

Interface and class hierarchy

An interface can extend any number of other interfaces. It inherits all of the methods and requirements from those other interfaces.

A class can only extend one other class, for the following reason: suppose a class C3 wants to extend both C1 and C2:

class C1 {
  int f(int x) {
    // do some things, possibly update variables to maintain a class invariant
  }
}

class C2 {
  int f(int x) {
    // do some other things
  }
}

When we call C3.f, what should happen? There is no clear way to merge the functions C1.f and C2.f. Java prevents this problem by disallowing multiple superclasses.

Is-A relationship

Both "extends" and "implements" establish an is-a relationship:

If every A is a B, then you can use an object of type A anywhere you expect an object of type B. For example, the following are both legal:

B x = new A();

void f(B x) { ... }
A y = new A();
f(y);

This doesn't work the other way; the following are illegal (again, assuming that "every A is-a B":

A x = new B(); // not OK; b's might not be A's

void f(A x) { ... }
B y = new B();
f(y); // not OK; f might use its argument as an A

Overriding methods

A class is allowed to "override" a method of its superclass. This means that it has a new implementation with the same name and argument types as the superclass.

The intent in this situation is that for Objects of the subclass, the operation is performed using the subclass's implementation of the method. You are providing a "more specific" way of completing the operation.

One way to describe this is using the bottom-up rule: when drawing the fields and methods inside an object, you would include both the superclass and the subclass's versions of the method, but when deciding which method actually runs, you search from the bottom and proceed upwards.

Often when overriding a method, you want to add some functionality to the superclass's version. In this case, you can refer to the superclass's version by "super.f(...)".

You can also call superclass constructors using the super(...) keyword.