Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage
<< | < | > | >> | API | Kapitel 9 - OOP III: Interfaces

## 9.1 Grundlagen

- 9.1 Grundlagen
  - 9.1.1 Definition eines Interfaces
  - 9.1.2 Implementierung eines Interfaces
  - 9.1.3 Verwenden eines Interfaces

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 Erben nichttrivialer 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 als wünschenswert an, daß Klassen eine oder mehrere Schnittstellendefinitionen erben können, und hat mit den Interfaces ein Ersatzkonstrukt geschaffen, das dieses Feature bietet.

### 9.1.1 Definition eines Interfaces

Ein Interface ist eine besondere Form einer Klasse, die ausschließlich abstrakte Methoden und Konstanten enthält. Anstelle des Schlüsselwortes `class` wird ein Interface mit dem Bezeichner `interface` deklariert. Alle Methoden eines Interfaces sind implizit abstrakt und öffentlich. Neben Methoden kann ein Interface auch Konstanten enthalten, die Definition von Konstruktoren ist allerdings nicht erlaubt.

Wir wollen uns ein einfaches Beispiel ansehen. Das folgende Listing definiert ein Interface `Groesse`, das die drei Methoden `laenge`, `hoehe` und `breite` enthält:

    001 /* Groesse.java */
    002
    003 public interface Groesse
    004 {
    005   public int laenge();
    006   public int hoehe();
    007   public int breite();
    008 }
| Groesse.java

Listing 9.1: Definition eines Interfaces

Diese Definition ähnelt sehr einer abstrakten Klasse und dient dazu, eine Schnittstelle für den Zugriff auf die räumliche Ausdehnung eines Objekts festzulegen. Wir wollen uns an dieser Stelle keine Gedanken über komplizierte Details wie zu verwendende Maßeinheiten oder Objekte mit mehr oder weniger als drei Dimensionen machen.

### 9.1.2 Implementierung eines Interfaces

Durch das bloße Definieren eines Interfaces wird die gewünschte Funktionalität aber noch nicht zur Verfügung gestellt, sondern lediglich beschrieben. Soll diese von einer Klasse tatsächlich realisiert werden, muß sie das Interface implementieren . Dazu erweitert sie die `class`-Anweisung um eine `implements`-Klausel, hinter der der Name des zu implementierenden Interfaces angegeben wird. Der Compiler sorgt dafür, daß alle im Interface geforderten Methoden definitionsgemäß implementiert werden. Zusätzlich "verleiht" er der Klasse einen neuen Datentyp, der - wie wir noch sehen werden - ähnliche Eigenschaften wie eine echte Klasse hat.

Eine beispielhafte Implementierung des Interfaces `Groesse` könnte etwa von der schon bekannten `Auto`-Klasse vorgenommen werden:

    001 /* Auto2.java */
    002
    003 public class Auto2
    004 implements Groesse
    005 {
    006   public String name;
    007   public int    erstzulassung;
    008   public int    leistung;
    009   public int    laenge;
    010   public int    hoehe;
    011   public int    breite;
    012
    013   public int laenge()
    014   {
    015     return this.laenge;
    016   }
    017
    018   public int hoehe()
    019   {
    020     return this.hoehe;
    021   }
    022
    023   public int breite()
    024   {
    025     return this.breite;
    026   }
    027 }
| Auto2.java

Listing 9.2: Implementierung eines Interfaces

Wir haben die Klasse dazu um drei veränderliche Instanzmerkmale erweitert, die es uns erlauben, die vom Interface geforderten Methoden auf einfache Weise zu implementieren. Ebenso wie die Klasse `Auto` könnte auch jede andere Klasse das Interface `` implementieren und so Informationen über seine räumliche Ausdehnung geben:

    001 public class FussballPlatz
    002 implements Groesse
    003 {
    004   public int laenge()
    005   {
    006     return 105000;
    007   }
    008
    009   public int hoehe()
    010   {
    011     return 0;
    012   }
    013
    014   public int breite()
    015   {
    016     return 70000;
    017   }
    018 }
| FussballPlatz.java

Listing 9.3: Die Klasse FussballPlatz

Hier geben die Interface-Methoden konstante Werte zurück (in der Annahme, daß alle Fußballplätze gleich groß sind). Ebenso gut ist es möglich, daß die Größe von anderen Instanzmerkmalen abhängig ist und zur Implementierung des Interfaces etwas mehr Aufwand getrieben werden muß:

    001 /* PapierBlatt.java */
    002
    003 public class PapierBlatt
    004 implements Groesse
    005 {
    006   public int format; //0=DIN A0, 1=DIN A1 usw.
    007
    008   public int laenge()
    009   {
    010     int ret = 0;
    011     if (format == 0) {
    012       ret = 1189;
    013     } else if (format == 1) {
    014       ret = 841;
    015     } else if (format == 2) {
    016       ret = 594;
    017     } else if (format == 3) {
    018       ret = 420;
    019     } else if (format == 4) {
    020       ret = 297;
    021     }
    022     //usw...
    023     return ret;
    024   }
    025
    026   public int hoehe()
    027   {
    028     return 0;
    029   }
    030
    031   public int breite()
    032   {
    033     int ret = 0;
    034     if (format == 0) {
    035       ret = 841;
    036     } else if (format == 1) {
    037       ret = 594;
    038     } else if (format == 2) {
    039       ret = 420;
    040     } else if (format == 3) {
    041       ret = 297;
    042     } else if (format == 4) {
    043       ret = 210;
    044     }
    045     //usw...
    046     return ret;
    047   }
    048 }
| PapierBlatt.java

Listing 9.4: Die Klasse PapierBlatt

|  | Die Art der Realisierung der vereinbarten Methoden spielt für das Implementieren eines Interfaces keine Rolle. Tatsächlich kommt es ausgesprochen häufig vor, daß Interfaces von sehr unterschiedlichen Klassen implementiert und die erforderlichen Methoden auf sehr unterschiedliche Weise realisiert werden. |  |
Hinweis

|  |  |  | Eine Klasse kann ein Interface auch dann implementieren, wenn sie nicht alle seine Methoden implementiert. In diesem Fall ist die Klasse allerdings als `abstract` zu deklarieren und kann nicht dazu verwendet werden, Objekte zu instanzieren. |  |
Tip

### 9.1.3 Verwenden eines Interfaces

Nützlich ist ein Interface immer dann, wenn Eigenschaften einer Klasse beschrieben werden sollen, die nicht direkt in seiner normalen Vererbungshierarchie abgebildet werden können. Hätten wir beispielsweise `Groesse` als abstrakte Klasse definiert, ergäbe sich eine sehr unnatürliche Ableitungshierarchie, wenn Autos, Fußballplätze und Papierblätter daraus abgeleitet wären. Durch Implementieren des `Groesse`-Interfaces können sie die Verfügbarkeit der drei Methoden `laenge`, `hoehe` und `breite` dagegen unabhängig von ihrer eigenen Vererbungslinie garantieren.

Wir wollen uns ein einfaches Beispiel für die Anwendung des Interfaces ansehen. Dazu soll eine Methode `grundflaeche` entwickelt werden, die zu jedem Objekt, das das Interface `Groesse` implementiert, dessen Grundfläche (Länge mal Breite) berechnet:

    001 /* Listing0905.java */
    002
    003 public class Listing0905
    004 {
    005   public static long grundflaeche(Groesse g)
    006   {
    007     return (long)g.laenge() * g.breite();
    008   }
    009
    010   public static void main(String[] args)
    011   {
    012     //Zuerst erzeugen wir ein Auto2...
    013     Auto2 auto = new Auto2();
    014     auto.laenge = 4235;
    015     auto.hoehe = 1650;
    016     auto.breite = 1820;
    017     //Nun ein DIN A4-Blatt...
    018     PapierBlatt blatt = new PapierBlatt();
    019     blatt.format = 4;
    020     //Und zum Schluß einen Fußballplatz...
    021     FussballPlatz platz = new FussballPlatz();
    022     //Nun werden sie ausgegeben
    023     System.out.println("Auto:  " + grundflaeche(auto));
    024     System.out.println("Blatt: " + grundflaeche(blatt));
    025     System.out.println("Platz: " + grundflaeche(platz));
    026   }
    027 }
| Listing0905.java

Listing 9.5: Verwendung eines Interfaces

Das Programm erzeugt zunächst einige Objekte, die das `Groesse`-Interface implementieren. Anschließend werden sie an die Methode `grundflaeche` übergeben, deren Argument `g` vom Typ `Groesse` ist. Durch diese Typisierung kann der Compiler sicherstellen, daß nur Objekte "des Typs" `Groesse` an `grundflaeche` übergeben werden. Das ist genau dann der Fall, wenn das übergebene Objekt dieses Interface implementiert.

Die Ausgabe des Programms ist:

    Auto:  7707700
    Blatt: 62370
    Platz: 7350000000

An diesem Beispiel kann man bereits die wichtigste Gemeinsamkeit zwischen abstrakten Klassen und Interfaces erkennen: Beide können im Programm zur Deklaration von lokalen Variablen, Membervariablen oder Methodenparametern verwendet werden. Eine Interface-Variable ist kompatibel zu allen Objekten, deren Klassen dieses Interface implementieren.

Auch der `instanceof`-Operator kann auf Interfacenamen angewendet werden. Eine alternative Implementierung der Methode `grundflaeche`, die mit allen Objekttypen funktioniert, könnte dann etwa so aussehen:

    001 public static long grundflaeche(Object o)
    002 {
    003   long ret = 0;
    004   if (o instanceof Groesse) {
    005     Groesse g = (Groesse)o;
    006     ret = (long)g.laenge() * g.breite();
    007   }
    008   return ret;
    009 }

Listing 9.6: Der instanceof-Operator auf Interfaces

Diese Implementierung verhält sich für alle Objekte, die das Interface `Groesse` implementieren, so wie die vorige. Für alle übrigen Objekte wird 0 zurückgegeben.

Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 3. Auflage, Addison Wesley, Version 3.0
<< | < | > | >> | API | © 1998-2002 Guido Krüger, http://www.javabuch.de