Es wurde bereits erwähnt, daß es in Java keine Mehrfachvererbung von Klassen gibt. Die möglichen Schwierigkeiten im Umgang mit mehrfacher Vererbung und die Einsicht, daß das Ererben von nichttrivialen Methoden aus mehr als einer Klasse in der Praxis selten zu erreichen ist, haben die Designer dazu veranlaßt, dieses Feature nicht zu realisieren. Andererseits sah man es sehr wohl als wünschenswert an, Methodendeklarationen von mehr als einer Klasse zu erben und hat mit den Interfaces ein Ersatzkonstrukt geschaffen, das genau dieses Feature bietet.
Ein Interface ist eine besondere Form einer Klasse, die ausschließlich abstrakte Methoden und Konstanten enthält. Anstelle von class wird zur Definition eines Interfaces das Schlüsselwort interface verwendet. Alle Methoden sind daraufhin standardmäßig abstrakt. Das folgende Listing definiert ein Interface Fortbewegungsmittel, das die Methoden kapazitaet und kilometerPreis definiert:
public interface Fortbewegungsmittel
{
public int kapazitaet();
public double kilometerPreis();
}
Was bei der Vererbung von Klassen als Ableitung bezeichnet wird, nennt man bei Interfaces Implementierung. Durch das Implementieren eines Interfaces verpflichtet sich die Klasse, alle Methoden, die im Interface definiert sind, zu implemetieren. Fehlt eine Methode, so gibt es einen Compilerfehler. Die Implementierung eines Interfaces wird durch das Schlüsselwort implements bei der Klassendefinition angezeigt.
Als Beispiel wollen wir die Klasse Auto um das neue Interface Fortbewegungsmittel erweitern und die Methoden kapazitaet und kilometerPreis implementieren:
public class Auto
implements Fortbewegungsmittel
{
public String name;
public int erstzulassung;
public int leistung;
private int anzahlSitze;
private double spritVerbrauch;
private double spritPreis;
public double kapazitaet()
{
return anzahlSitze;
}
public double kilometerPreis()
{
return spritVerbrauch*spritPreis/100;
}
}
Ebenso wie die Klasse Auto könnte auch jede andere Klasse das Interface Fortbewegungsmittel implementieren und Konkretisierungen der beiden Methoden vornehmen. Nützlich ist dies insbesondere für Klassen, die in keinem direkten Zusammenhang mit der Klasse Auto und ihrer Vererbungshierarchie stehen. Um ihr gewisse Eigenschaften eines Fortbewegungsmittels zu verleihen, könnte also beispielsweise auch die Klasse Teppich dieses Interface implementieren.
Das folgende Interface Sammlerstueck mag bei gewöhnlichen Autos keine Anwendung finden, ist bei einem Oldtimer aber durchaus sinnvoll:
public Interface Sammlerstueck
{
public double sammlerWert();
public String bisherigeAusstellungen();
}
public class Oldtimer
extends Auto
implements Sammlerstueck
{
//...
}
Da ein Sammlerstueck aber durchaus auch in ganz anderen Vererbungshierarchien auftauchen kann als bei Autos (beispielsweise bei Briefmarken, Schmuck oder Telefonkarten), macht es keinen Sinn, diese Methoden in den Ableitungsbäumen all dieser Klassen wiederholt zu deklarieren. Statt dessen sollten die Klassen das Interface Sammlerstueck implementieren und so garantieren, daß die Methoden sammlerWert und bisherigeAusstellungen zur Verfügung stehen.
Eine Klasse kann nicht nur ein einzelnes, sondern eine beliebige Anzahl an Interfaces implementieren. So ist es beispielsweise problemlos möglich, eine aus Flugzeug abgeleitete Klasse Doppeldecker zu definieren, die sowohl Sammlerstueck als auch Fortbewegungsmittel implementiert:
public class Doppeldecker
extends Flugobjekt
implements Fortbewegungsmittel, Sammlerstueck
{
//...
}
Die Klasse Doppeldecker muß dann alle in Sammlerobjekt und Fortbewegungsmittel deklarierten Methoden implementieren.
Interfaces besitzen zwei wichtige Eigenschaften, die auch Klassen haben:
Neben abstrakten Methoden können Interfaces auch Konstanten, also Variablen mit den Attributen static und final, enthalten. Wenn eine Klasse ein solches Interface implementiert, erbt es gleichzeitig auch all seine Konstanten. Es ist auch erlaubt, daß ein Interface ausschließlich Konstanten enthält.
Dieses Feature kann zum Beispiel nützlich sein, wenn ein Programm sehr viele Konstanten definiert. Anstatt diese in ihren korrespondierenden Klassen zu belassen und mit Klasse.Name aufzurufen, könnte ein einzelnes Interface definiert werden, das alle Konstantendefinitionen vereinigt. Wenn nun jede Klasse, die eine der Konstanten benötigt, dieses Interface implementiert, so stehen alle darin definierten Konstanten direkt zur Verfügung und können ohne die Qualifizierung mit einem Klassennamen aufgerufen werden.
Die Java-Klassenbibliothek definiert und implementiert selbst eine ganze Reihe von Interfaces. Drei von ihnen sollen hier kurz vorgestellt werden.
Das Interface Runnable wird zur Implementierung von Threads verwendet, und zwar insbesondere dann, wenn die betreffende Klasse selbst nicht direkt aus Thread abgeleitet werden kann. Runnable deklariert lediglich die Methode run, deren Rumpf den ausführbaren Code des Threads enthält. Der Umgang mit Threads wird in Kapitel 10 erklärt.
Das Interface Enumeration wird zur Deklaration von Enumeratoren verwendet. Enumeratoren dienen dazu, alle Elemente von Kollektionen (z.B. Hashtable oder Vector) nacheinander zu durchlaufen. Enumeration definiert die Methoden nextElement und hasMoreElements. Sie dienen dazu, das nächste Element der Kollektion zu holen bzw. zu testen, ob weitere Elemente vorhanden sind.
Dieses Interface ist etwas untypisch, denn es besitzt weder Methoden noch Konstanten. Es dient lediglich dazu, dem Compiler anzuzeigen, daß eine Klasse die Methode clone unterstützt und damit die Fähigkeit besitzt, sich selbst zu kopieren.