Next Previous Up Top


4 Class Relationships

4.8 Interfaces


4.8 Interfaces


We have seen that the idea of a class having a public interface which is inherited from a superclass is important because of the ability to substitute objects of different types. This means we can write code in terms of the interface specified by a superclass class but expect to be able to 'substitute' an object of any subclass type. Java takes this idea further by supporting interface declarations, which, as the name implies, allows an interface to be declared without having to declare a class.

The distinction between an interface and a class is, at first sight, subtle but nonetheless important. A class can declare variables, methods, method implementations, and, unless abstract, can have instance objects. An interface, on the other hand, is limited to declaring certain kinds of variables and methods. It cannot be used to declare method bodies and cannot have instance objects. In some sense, an interface is an abstract class of a specialized form. In particular, an interface is like an abstract class that can have no implementation information, only method signatures and certain sorts of variable declaration. The purpose of an interface is to declare only a public interface. This is the nearest thing that Java has to an abstract data type (see Chapter 3.4, Page 32) and indeed, an interface introduces a new type into a Java program in the same way that an abstract class does.

An interface is declared much like a class:

interface QueueOfInt
{
	public void addback(int x) ;
	public int removefront() ;
}

Here a QueueOfInt interface is declared, providing declarations for methods to add or remove integers but no method bodies, only signatures.

Interfaces cannot be inherited from to create a class. We can inherit from an interface but only to create another interface; interfaces form a completely separate inheritance hierarchy. The relationship between interfaces and classes is that a class can implement an interface. So, for example:

class IntQueue implements QueueOfInt
{
	public void addback(int x) { ... }
	public int removefront() { ... }

	private int[] items ;
}

The keyword implements denotes that the IntQueue class is guaranteeing to implement the public interface that the QueueOfInt interface demands. It must therefore provide implementations all the methods declared by the interface QueueOfInt, by providing declarations with method bodies for each of them.

An IntDequeue class can also be declared as implementing the interface:

class IntDequeue extends IntQueue implements QueueOfInt
{
	public void addfront(int x) { ... }
	public int removeback() { ... }

	private int[] items ;
}

Alternatively, we could have the following:

class IntDequeue implements QueueOfInt
{
	public void addfront(int x) { ... }
	public void addback(int x) { ... }
	public int removeback() { ... }
	public int removefront() { ... }

	private List items ;
}

In this approach to implementing these classes, IntQueue and IntDequeue are no longer related by inheritance. Thus, the Dequeue has to declare all of its methods and its own data structure -- which, by way of contrast, is shown as a data type known as List.

As mentioned above, like a class, an interface declares a new type and this means that variables of the interface type can be declared. If two or more otherwise unrelated classes implement an interface, their objects can be referenced by variables of the interface type. So, given the declaration of IntQueue and one of the declarations of IntDequeue above, it is possible to declare a variable of type QueueOfInt and assign to it either an IntQueue object or an IntDequeue object.

IntQueue a = new IntQueue() ;
IntDequeue b = new IntDequeue() ;
QueueOfInt q ;					// Declare variable of interface type
...
q = a ;					// Make q refer to an actual object ...
q.addback(1) ;					// ... and make use of the object.
int i = q.removefront() ;
...
q = b;					// Make q refer to another actual object ...
q.addback(1) ;					// ... and make use of it.
i = q.removefront() ;

Whether or not IntQueue and IntDequeue are related by inheritance, both implement the interface QueueOfInt, both conform to the type specified by that interface. This mechanism allows Java to support what is known as type conformance. An object may conform or fit to a type specified independently of class-based inheritance[20].

A Java class can only inherit from one superclass; Java does not support multiple inheritance of classes, i.e. inheriting from more than one superclass). Implementing full multiple inheritance (as is done in C++) leads to a relatively disproportionate amount of language syntax and semantics. By having only single inheritance of classes, Java is made a smaller and simpler language. Sometimes though it is very desirable to inherit from more than one superclass. Having said this, many, in particular the designers of Java, observe that most uses of multiple inheritance are to allow the subclass to support several public interfaces: multiple inheritance is a very useful and valuable tool, its major useful use being type conformance. Having interfaces with type conformance and single inheritance allows Java to support this principal use of multiple inheritance in other languages without the complexity of a full multiple inheritance system. To ensure full support for the type conformance system a class may, whilst only able to inherit from a single superclass, implement many interfaces thereby providing the mechanism that provides some of the features of multiple inheritance, although only for interface information and not for implementation.

Some argue that restricting Java in this way is a great shame; the designers of Java felt that the other uses of multiple inheritance did not warrant the complexity in the language and compiler. For the moment, the debate rages.


[20] Java still requires a class to be explicitly declared as implementing an interface. Some languages remove even this requirement.

Copyright 1997 Russel Winder and Graham Roberts

Last updated: 6 Oct 1997