Helmichs Informatik-Lexikon

Interface

Mit Hilfe von Interfaces kann man in Java eine Mehrfachvererbung simulieren. Eine "richtige" Mehrfachvererbung (siehe Vererbung) ist in Java zwar nicht möglich, aber mit Hilfe der Interface-Technik kommt man der Mehrfachvererbung schon recht nahe.

Vererbung

Vielleicht lesen Sie sich zum besseren Verständnis zunächst den Lexikon-Eintrag über Vererbung durch. In diesem Artikel werde ich mich nämlich auf die dort genannten Beispiele beziehen.

Betrachten wir nun folgendes Bild:

Beschreibung siehe folgenden Text

Ein BlueJ-Klassendiagramm mit sieben Klassen
Autor: Ulrich Helmich 2017, Lizenz: siehe Seitenende

Wir sehen hier zunächst sieben Java-Klassen, von denen aber zwei als <<interface>> charakterisiert sind.

Die Oberklasse ist Tier, zwei Unterklassen Vogel und Saeuger erben alle nicht-privaten Attribute und Methoden von Tier. Die Unterklasse Saeuger wiederum ist Mutterklasse von den beiden Unterklassen Wolf und Schaf.

So weit, so gut, wir haben es bisher mit einer normalen Vererbung in Java zu tun. Was aber hat es jetzt mit den beiden Interfaces Fleischfresser und Pflanzenfresser auf sich?

Der Quelltext von Fleischfresser könnte so aussehen:

public interface Fleischfresser
{
   void jagen();
   void fressen();
}

Auf den ersten Blick handelt es sich um die Definition und Implementation einer Java-Klasse mit zwei Methoden und ohne Attribute. Bei näherem Hinsehen entdeckt man das Schlüsselwort interface.

Dieses Schlüsselwort bewirkt nun, dass alle Methoden der Java-Klasse Fleischfresser als abstrakte Methoden behandelt werden, ohne dass hierfür das Schlüsselwort abstract notwendig ist. Die beiden Methoden jagen() und fressen() sind also abstrakte Methoden. Das heißt, sie werden im Interface Fleischfresser nicht implementiert, sondern nur "angedeutet" bzw. "vorgeschrieben".

Das Wort "vorgeschrieben" ist hier tatsächlich sinnvoll. Die Klasse Wolf, die das Interface Fleischfresser mit Hilfe des Schlüsselworts implements einbindet, muss nämlich jetzt die beiden abstrakten Methoden implementieren. Sollte jemand auf die kühne Idee kommen, die Klasse Wolf folgendermaßen zu programmieren:

public class Wolf extends Saeuger implements Fleischfresser
{
    public Wolf()
    {
        super("dunkelgrau");
    }
   
    // weitere Methoden ...
}

dann würde der Compiler eine Fehlermeldung anzeigen: "Wolf is not abstract and does not override abstract method fressen() in Fleischfresser".

Eine ähnliche Fehlermeldung erhält man auch immer, wenn man das Interface ActionListener in eine Java-Anwendung einbindet und dann die Methode actionPerformed() nicht implementiert, die von dem Interface zwingend vorgeschrieben wird.

Die Klasse Wolf muss also die beiden vom Interface vorgeschriebenen Methoden zwingend implementieren:

public class Wolf extends Saeuger implements Fleischfresser
{
    public Wolf()
    {
        super("dunkelgrau");
    }
    
    public void jagen()
    {
    }

    public void fressen()
    {
    }
}

Noch sind diese Methoden leer, aber das stört den Compiler nicht. Die Klasse kann jetzt jedenfalls fehlerfrei kompiliert werden.

Da die beiden Methoden des Interfaces abstrakt sind, also nicht implementiert sind, erbt die Klasse Wolf eigentlich nichts von der Klasse Fleischfresser, zumindest wird keine Funktionalität (keine Algorithmen) geerbt. Das Einzige, was Wolf vom Interface erbt, ist die Anweisung, zwei Methoden jagen() und fressen() des Typs void zu implementieren. Wie Wolf diese beiden Methoden implementiert, ist dem Interface völlig egal, solange die Signatur der Wolf-Methoden mit der Signatur der Fleischfresser-Methoden übereinstimmt.

Die Klasse Schaf bindet stattdessen das Interface Pflanzenfresser ein, das andere Methoden als das Interface Fleischfresser zur Verfügung stellt - nein, das ist nicht das richtige Wort - vorschreibt ist zutreffender. Das Interface stellt ja keine funktionierenden Methoden zur Verfügung, sondern schreibt lediglich vor, dass die Klasse, die das Interface einbindet, diese Methoden implementieren muss. Und zwar genau so, wie es im Interface vorgegeben ist.

Interface-Technik im Informatik-Unterricht

Im Informatik-Unterricht der Q1 oder Q2 und auch im Informatik-Abitur tauchen Interfaces spätestens dann auf, wenn es um lineare Listen und binäre Suchbäume geht. Ein binärer Suchbaum soll ja nicht nur simple int-Zahlen speichern, sondern im Idealfall Objekte beliebiger Klassen. Nun muss aber bei jedem neu einzufügenden Objekt überprüft werden, ob dieses in den linken oder in den rechten Teilbaum eingefügt werden muss. Dazu muss das neue Objekt mit der jeweiligen Wurzel des Baums bzw. Teilbaums verglichen werden. Bei int-Zahlen ist das noch ganz einfach, aber wie ist beispielsweise zu verfahren, wenn es sich bei den Objekten um komplexe Personaldaten handelt? Welche der vielen Eigenschaften der Objekte soll für das Einordnen Ausschlag gebend sein?

Um diese wichtige Frage zu regeln, gibt es normalerweise ein Interface, das Vergleichsoperationen wie less(), greater(), equal(), notequal etc. vorschreibt. Diejenige Klasse, deren Objekte in den Baum eingefügt werden sollen, bindet dieses Interface dann ein und muss diese vorgeschriebenen Vergleichsoperationen implementieren. Dann ist die Klasse, die den eigentlichen Baum implementiert, in der Lage, die einzufügenden Knoten zu vergleichen.

Ausschnitt aus der Dokumentation einer Abituraufgabe von 2021

Im Informatik-Abitur 2021 des Landes NRW kam - wie immer - auch eine Aufgabe zum Thema binärer Suchbaum vor. In der Dokumentation der generischen Klasse BinarySearchTree, die den Schüler(innen) zur Verfügung stand, ist auch die Rede von einem Interface. Und zwar müssen die Objekte, die in dem Baum verwaltet werden sollen, das Interface ComparableContent implementieren, das die drei Methoden isLess(), isEqual() und isGreater() vorschreibt.