Wir behandeln in diesem Beitrag die wichtigsten Eigenschaften vom Interfaces in Java.
Dabei soll dieser allgemeine Beitrag über Interfaces als Übersicht dienen. Ausführliche Erklärungen zu den einzelnen Themen findet ihr in den jeweiligen Themenbeitrag:
- Interfaces – Verwendung von Schnittstellen in Java
- Implementieren von mehreren Interfaces am Beispiel des Comparable-Interfaces
- Vererbung von Interfaces in Java
- Ein Interface an ein anderes Interface vererben – Ableiten von Interfaces in Java
- Konstanten in Java verfügbar machen – Interface vs. import static
- Mit Hilfe von Interfaces Funktionszeiger in Java nachbilden
- Interface als Flag nutzen am Beispiel des Cloneable-Interfaces in Java
- Implementierung des Interface an die Default-Klasse delegieren in Java
Was ist ein Interface in Java und wofür wird es genutzt?
In Java kann eine abgeleitete Klasse nur genau eine Vaterklasse haben. Das Erben von mehr als einer Superklasse (Mehrfachvererbung) ist nicht möglich.
Den Entwicklern der Programmiersprache Java war Mehrfachvererbung bekannt und auch die Nachteile die sie mit sich bringt. Ganz auf Mehrfachvererbung verzichten wollten sie aber nicht und schafften einen Mittelweg: das Interface.
Interfaces können als Ersatzkonstrukt für Mehrfachvererbung gesehen werden. Eine Klasse kann mehrere Interfaces implementieren, d.h. ihr können mehrere Schnittstellen zur Verfügung gestellt werden.
Jede dieser Schnittstellen (Interfaces) muss aber von der Klasse vollständig implementiert werden.
Ein Interface ist eine Schnittstelle, über die einer Klasse bestimmte Funktionen zur Verfügung gestellt werden.
Um die Funktionen nutzen zu können, müssen sie aber erst von der Klasse implementiert werden. Das Interface gibt nur den Rahmen (die Methodendeklarationen) vor.
Interfaces können als eine besondere Form einer Klasse angesehen werden. Sie enthalten ausschließlich Konstanten und abstrakte Methoden. Die abstrakten Methoden müssen von der Klasse implementiert werden, der das Interface zugewiesen wird.
Mehrfachimplementierung – Implementieren von mehreren Interfaces
In Java gibt es keine Mehrfachvererbung. Als Alternative zur Mehrfachvererbung können wir auf Interfaces zurückgreifen, da eine Klasse mehr als ein Interface implementieren kann.
Die Mehrfachimplementierung von verschiedenen Interfaces ist in Java durchaus üblich. Dabei muss jede Klasse für jedes Interface alle darin deklarierten Methoden implementieren.
Implementiert eine Klasse ein Interface, wird sie dabei automatisch zu diesem Interface-Datentyp kompatibel. Eine Klasse kann daher zu mehreren Datentypen kompatibel sein.
In Java ist eine Klasse kompatibel zu:
- dem Datentyp ihrer eigenen Klasse
- den Datentypen ihrer n-Superklassen
- den Datentypen ihrer m-Interfaces, die sie implementiert
Somit ist eine Klasse zu (n + m + 1) Datentypen kompatibel.
Mit Hilfe von Interfaces werden Eigenschaften ausgedrückt, die auf Klassen aus unterschiedlichen Klassenhierarchien zutreffen. Daher werden Interfaces oft mit Hilfe von substantivierten Adjektiven (Eigenschaftswörtern) benannt, so wie das Interface Comparable aus der Java-Klassenbibliothek, das sich in dem Paket java.lang
befindet.
Das Comparable-Interface enthält die abtrakte Methode compareTo(Object obj), die einen int-Wert zurückliefert. Die Methode vergleicht das aufgerufene Objekt mit dem als Argument übergebene Objekt. Die Klassen String und Character implementieren das Comparable-Interface standardmäßig.
Wir wollen nun das Comparable-Interface dazu verwenden, um unsere Klassen miteinander vergleichbar zu machen. Dazu werden wir das Interface für unsere Klassen implementieren. Das zweite Interface Transportierbar haben wir bereits implementiert und können daher die Mehrfachimplementierung von Interfaces direkt testen.
Vererbung von Interfaces in Java
In Java vererbt eine Vaterklasse ihre Interfaces an ihre Tochterklassen. Sind von der Vaterklasse bereits alle Interface-Methoden implementiert worden, muss in den Tochterklassen keine Methode zusätzlich nachimplementiert werden.
Es ist aber erlaubt, dass die Tochterklassen eigene Versionen der Interface-Methoden implementieren, falls dies erforderlich sein sollte.
Wir möchten nun das Vererben von Interfaces in Java testen, dazu schreiben wir eine kleine Anwendung, bei der das Interface der Vaterklasse an die abgeleitete Tochterklasse vererbt wird. Die Tochterklasse soll dabei noch ein zweites Interface zusätzlich implementieren und eine Interface-Methode der Vaterklasse überlagern.
Die neue Klasse erbt dabei das Interface Transportierbar von ihrer Vaterklasse. Da die Vaterklasse bereits alle Interface-Methoden implementiert hat, kann das geerbte Interface sofort verwendet werden. Wir haben aber extra eine kleine Änderung an der Interface-Methode vorgenommen und sie neu implementiert. Somit wird die Interface-Methode der Vaterklasse jetzt von unserer neuen Methodenversion der Tochterklasse überlagert.
Ableiten von Interfaces in Java
In Java kann ein Interface seine Eigenschaften an andere Interfaces vererben. Wie bei der normalen Vererbung erbt dabei das abgeleitete Interface alle Methoden und Konstanten des Basis-Interfaces.
Soll eine Klasse ein abgeleitetes Interface implementieren, muss sie alle Methoden dieses Interfaces und des übergeordneten Super-Interfaces implementieren. Wie Interfaces von anderen Interfaces ableitet erfahrt ihr in unserem Beitrag: Ableiten von Interfaces in Java.
Um ein Interface von einem anderen abzuleiten wird das Schlüsselwort extends genutzt. Wie bei Klassen erbt das neue Interface dann alle Eigenschaften des Vater-Interfaces. Es können dann anschließend die geerbten Methoden übernommen oder neu implementiert werden. Natürlich können auch neue Methoden implementiert werden, die im Vater-Interface noch nicht existiert haben.
Konstanten in Java verfügbar machen – Interface vs. import static
Komplexe Java-Anwendungen müssen oft auf Konstanten zugreifen. Dabei kann es vorkommen, dass viele Konstanten in mehreren Klassen zur Verfügung stehen müssen.
Möchte man in Java oft benötigte Konstanten in unterschiedlichen Klassen wiederverwenden, dann bieten sich hierfür die folgenden beiden Vorgehensweisen an:
- Alle Konstanten mit Hilfe eines Interfaces den Klassen zur Verfügung stellen oder
- Nur die benötigten Konstanten mittels import static in den Klassen verfügbar machen
Wir möchten in dem Beitrag: Konstanten in Java verfügbar machen beide Vorgehensweisen vorstellen. Zur Info vorab: Der Ansatz, Konstanten mit Hilfe des import static Klassen gezielt zur Verfügung zu stellen, ist der bessere Programmierstil, wenngleich das Nutzen eines Interfaces auch eine, unter Java-Programmierern, sehr weit verbreitete Vorgehensweise ist.
In Java können Interfaces auch ohne Methoden definiert werden. Sie bestehen dann nur aus Konstanten. Es ist jedoch zu beachten, dass sie Konstanten als static final deklariert werden müssen.
Sehr hilfreich ist in diesem Fall, dass in Java in einem Interface alle Konstanten implizit als public static final deklariert sind. Daher müssen die Modifier nicht extra angegeben werden. Diese Aufgabe übernimmt dann der Java-Compiler.
Konstanten können in Klassen auch als static final und als final deklariert werden. In Interfaces sind Konstanten jedoch immer implizit als public static final deklariert. Der Grund dafür ist, dass von einem Interface selbst kein konkretes Objekt erstellt werden kann.
Methoden sind in Interfaces immer implizit als public abstract deklariert und müssen später von einer Klasse implementiert werden, um mit Hilfe eines Objekts konkret werden zu können. Außerdem ist es nicht möglich Interface-Methoden als static zu deklarieren.
Wenn eine Klasse ein Interface implementiert, erbt sie alle Konstanten von dem Interface. Da ein Interface von mehr als einer Klasse implementiert werden kann, erhalten alle Klassen, die dieses Interface implementieren, die Konstanten des Interfaces. Diese Eigenschaft wird oft genutzt und üblicherweise ein Interface definiert, das eine Sammlung von oft benötigten Konstanten enthält. Alle Klassen, die auf diese Konstanten zugreifen müssen, können einfach dieses Interface implementieren und erhalten somit direkten Zugriff auf die darin definierten Konstanten.
Auf die vom Interface geerbten Konstanten kann unqualifiziert zugegriffen werden. Als Zugriff kann anstelle des qualifizierten Zugriffs: Interfacename.Konstantenname
der unqualifizierte, direkte Zugriff: Konstantenname
verwendet werden.
Mit Hilfe von Interfaces Funktionszeiger in Java nachbilden
In Java können Interfaces zur Nachbildung von Funktionszeigern verwendet werden. Diese Anwendungsart von Interfaces ist besonders nützlich, wenn die Konfigurationsanforderungen einer Methode nicht durch die Übergabe von Variablen erfüllt werden können.
Die hier beschriebene Vorgehensweise wird bspw. bei Callback-Funktionen benötigt, wie sie für die Programmierung von grafischen Benutzeroberflächen verwendet werden.
Die Vorgehensweise für die Nachbildung von Funktionszeigern mittels Interfaces
Als erstes wird ein Interface definiert, in dem die erforderliche Methode abstrakt deklariert wird.
Dieses Interface wird anschließend von unterschiedlichen Klassen implementiert und dabei wird die Interface-Methode jeweils so umgesetzt, dass in ihr die erforderlichen Berechnungen durchgeführt werden.
Somit verfügt jede Klasse über eine eigene, an die Konfigurationsanforderungen angepasste, Implementierung der Interface-Methode. Werden nun Instanzen dieser Klassen erzeugt und an die zu konfigurierende Methode übergeben, so wird in der zu konfigurierenden Methode jeweils genau die Methodenversion der jeweils instanzierten Klasse ausgeführt. Die jeweilige Methodenversion kann dadurch den Konfigurationsanforderungen entsprechend implementiert werden.
Diese Vorgehensweise kann am besten mit der Beispielanwendung, wie man mit Hilfe von Interfaces Funktionszeiger in Java nachbildet, verstanden werden.
Interface als Flag nutzen am Beispiel des Cloneable-Interfaces in Java
In Java können Interfaces als logische An- und Aus-Schalter verwendet werden, die zur Kompilier- und Laufzeit abgefragt werden können.
In dem Beitrag: Interface als Flag nutzen möchten wir ein solches Flag-Interface am Beispiel des Interface Cloneable vorstellen. Das Cloneable-Interface ist ein Flag (An/Aus-Schalter) für die in der Klasse Object implementierte Methode clone(). Die Methode clone() ist in der Object-Klasse native implementiert, um die Performance zu verbessern.
Das Cloneable-Interface wird als Indikator genutzt, mit dem überprüft wird, ob eine abgeleitete Klasse die Fähigkeit besitzt eine eigene Objektkopie herzustellen. Ist das Cloneable-Interface in der abgeleiteten Klasse nicht implementiert, wird beim Aufruf von clone() eine CloneNotSupportedException ausgelöst.
Dadurch wird dem Entwickler mitgeteilt, dass die abgeleitete Klasse nicht über die Fähigkeit verfügt eigene Objekte elementweise zu kopieren (clonen).
Implementierung des Interface an die Default-Klasse delegieren in Java
In dem Beitrag: Implementierung des Interface an die Default-Klasse delegieren werden wir eine Default-Implementierung eines Interfaces entwickeln und die Funktionalität dieser Default-Implementierung in einer Delegationsklasse wiederverwenden.
Die Idee dabei ist, die Vorteile eines Interfaces (Mehrfachverwendung in unterschiedlichen Klassenhierarchien) mit denen einer Klasse (Teilimplementierung der Methoden, Verwendung von Konstruktoren und statischen Elementen) zu kombinieren.
Dabei stellt ein Interface die Methodendeklarationen zur Verfügung und eine Default-Klasse implementiert diese Methoden als Referenzimplementierung (Default-Implementierung).
Der folgende Link führt euch zurück zum Programmierkurs.
Comments 4
Hi,
gratuliere zur Website und zum Tutorial. Selten so etwas Gutes gefunden. Zumindest die Finance-App hab ich mir schon angeschaut. Bin begeistert.
lg, Andreas
Author
Hallo Andreas,
vielen Dank für Dein Lob! Hat mich sehr gefreut 🙂
Viele Grüße, Chris
Pingback: Einführung in Java Design Patterns (Software Entwurfsmuster)
Pingback: Vererbung und Polymorphismus in Java