|
Es wurde bereits erwähnt, daß es in Java keine Mehrfachvererbung von Klassen gibt. Die möglichen Schwierigkeiten beim Umgang mit mehrfacher Vererbung und die Einsicht, daß das Ererben von nichttrivialen Methoden aus mehr als einer Klasse in der Praxis selten zu realisieren ist, haben die Designer dazu veranlaßt, dieses Feature nicht zu implementieren. 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. Ein Interface kann allerdings keine Konstruktoren enthalten.
Das folgende Listing definiert ein Interface Fortbewegungsmittel, das die Methoden kapazitaet und kilometerPreis definiert:
001 public interface Fortbewegungsmittel 002 { 003 public int kapazitaet(); 004 public double kilometerPreis(); 005 } |
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 implementieren. Fehlt eine Methode, so gibt es einen Compilerfehler. Die Implementierung eines Interfaces wird durch das Schlüsselwort implements bei der Klassendefinition angezeigt.
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.
Eine Klasse kann auch dann ein Interface implementieren, wenn sie bereits von einer anderen Klasse abgeleitet ist. In diesem Fall erbt die neue Klasse wie gewohnt alle Eigenschaften der Basisklasse und hat zusätzlich die Aufgabe, die abstrakten Methoden des Interfaces zu implementieren. |
![]() |
|
![]() |
Das folgende Interface Sammlerstueck mag bei gewöhnlichen Autos keine Anwendung finden, ist bei einem Oldtimer aber durchaus sinnvoll:
|
![]() |
|
![]() |
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 Interface, sondern eine beliebige Anzahl von ihnen implementieren. So ist es beispielsweise problemlos möglich, eine aus Flugzeug abgeleitete Klasse Doppeldecker zu definieren, die sowohl Sammlerstueck als auch Fortbewegungsmittel implementiert:
001 public class Doppeldecker 002 extends Flugobjekt 003 implements Fortbewegungsmittel, Sammlerstueck 004 { 005 //... 006 } |
Die Klasse Doppeldecker muß dann alle in Sammlerobjekt und Fortbewegungsmittel deklarierten Methoden implementieren.
Interfaces besitzen zwei wichtige Eigenschaften, die auch Klassen haben:
Es macht also Sinn, ein Interface als eine Typvereinbarung anzusehen. Eine Klasse, die dieses Interface implementiert, ist dann vom Typ des Interfaces. Wegen der Mehrfachvererbung von Interfaces kann eine Instanzvariable damit insbesondere mehrere Typen haben und zu mehr als einem Typen zuweisungskompatibel sein. |
![]() |
|
![]() |
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.
|
Go To Java 2, Addison Wesley, Version 1.0.2, © 1999 Guido Krüger, http://www.gkrueger.com |