Interfaces und deren Anwendung

Werbung
Programmieren / Java
Interfaces und deren Anwendung
Frage Nr. 5
I nt e r fa ce s und de r e n
Anw e nd ung
Ausarbeitung einer Maturafrage aus dem Fach
P r o g r a m m i e r e n
/
Andreas Hechenblaickner
5CDH | HTBLA Kaindorf/Sulm
Dipl.-Ing. Manfred Wilfling
Erstelldatum: 23. Februar 2003
Letzte Überarbeitung: 2. Juni 2003
J a v a
PUC | Interfaces und deren Anwendung
Inhaltsverzeichnis
Inhaltsverzeichnis
Inhaltsverzeichnis
2
1
Grundlagen
3
1.1
Definition eines Interfaces
3
1.2
Implementierung eines Interfaces
3
1.3
Verwendung eines Interfaces
5
1.4
Mehrfachimplementierung
6
1.5
Vererbung von Interfaces
7
1.6
Ableiten von Interfaces
8
2
Anwendung von Interfaces
9
2.1
Konstanten in Interfaces
9
2.2
Implementierung von Flags
9
2.3
Nachbildung von Funktionszeigern
10
Folienvorschlag
12
Zusammenfassung
13
Quellen
13
HTBLA Kaindorf/Sulm
Seite 2 von 13
PUC | Interfaces und deren Anwendung
1
Grundlagen
Grundlagen
In Java gibt es keine Mehrfachvererbung von Klassen. Die möglichen Schwierigkeiten beim Umgang mit mehrfacher Vererbung haben die Designer dazu
veranlasst, dieses Feature nicht zu implementieren. Andererseits sah man es
als wünschenswert an, dass Klassen eine oder mehrere Schnittstellendefinitionen erben können, und hat mit den Interfaces ein Ersatzkonstrukt geschaffen,
das dieses Feature bietet.
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.
Das folgende Listing definiert ein Interface Groesse, das die drei Methoden
laenge(), hoehe() und breite() enthält:
1 public interface Groesse {
2
public int laenge();
3
public int hoehe();
4
public int breite();
5 }
«interface»
Groesse
+laenge()
+hoehe()
+breite()
Abbildung 1: UML-Notation des Interfaces Groesse
Diese Definition ähnelt sehr einer abstrakten Klasse und dient dazu, eine
Schnittstelle für den Zugriff auf die räumliche Ausdehnung eines Objekts festzulegen.
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, muss sie das Interface implementieren. Dazu erweitert sie die class-Anweisung um eine implementsKlausel, hinter der der Name des zu implementierenden Interfaces angegeben
wird.
HTBLA Kaindorf/Sulm
Seite 3 von 13
PUC | Interfaces und deren Anwendung
Grundlagen
1 public class Auto implements Groesse {
2
public String name;
3
public int
leistung;
4
public int
laenge;
5
public int
hoehe;
6
public int
breite;
7
8
public int laenge() {
9
return this.laenge;
10
}
11
12
public int hoehe() {
13
return this.hoehe;
14
}
15
16
public int breite() {
17
return this.breite;
18
}
19 }
Auto
«interface»
Groesse
+laenge()
+hoehe()
+breite()
name: String
leistung: int
laenge: int
hoehe: int
breite: int
+laenge()
+hoehe()
+breite()
Abbildung 2: UML-Notation der Klasse Auto
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:
1 public class FussballPlatz implements Groesse {
2
public int laenge() {
3
return 105000;
4
}
5
6
public int hoehe() {
7
return 0;
8
}
9
10
public int breite() {
11
return 70000;
12
}
13 }
«interface»
Groesse
+laenge()
+hoehe()
+breite()
Fussballplatz
+laenge()
+hoehe()
+breite()
Abbildung 3: UML-Notation der Klasse Fussballplatz
HTBLA Kaindorf/Sulm
Seite 4 von 13
PUC | Interfaces und deren Anwendung
Grundlagen
Die Art der Realisierung der vereinbarten Methoden spielt für das Implementieren eines Interfaces keine Rolle. Tatsächlich kommt es ausgesprochen häufig
vor, dass Interfaces von sehr unterschiedlichen Klassen implementiert und die
erforderlichen Methoden auf sehr unterschiedliche Weise realisiert werden.
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.
1.3
Verwendung 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 und Fußballplätze 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.
1 public class InterfaceDemo1 {
2
public static long grundflaeche(Groesse g) {
3
return (long)g.laenge() * g.breite();
4
}
5
6
public static void main(String[] args) {
7
//Zuerst erzeugen wir ein Auto...
8
Auto auto = new Auto();
9
auto.laenge = 4235;
10
auto.hoehe = 1650;
11
auto.breite = 1820;
12
13
//Und zum Schluß einen Fußballplatz...
14
FussballPlatz platz = new FussballPlatz();
15
16
//Nun werden sie ausgegeben
17
System.out.println("Auto: " + grundflaeche(auto));
18
System.out.println("Platz: " + grundflaeche(platz));
19
}
20 }
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, dass nur Objekte "des Typs" Groesse an grundflaeche übergeben werden. Das ist genau dann der Fall, wenn das übergebene
Objekt dieses Interface implementiert.
HTBLA Kaindorf/Sulm
Seite 5 von 13
PUC | Interfaces und deren Anwendung
Grundlagen
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.
1.4
Mehrfachimplementierung
Es ist durchaus möglich (und gebräuchlich), dass eine Klasse mehrere Interfaces implementiert. Sie muss dann zu jedem Interface alle darin definierten
Methoden implementieren. Mit jedem implementierten Interface wird sie zu
dem dadurch definierten Datentyp kompatibel. Eine Klasse, die n Interfaces
implementiert, ist demnach zu n + 1 Datentypen (plus ihren jeweiligen Oberklassen) kompatibel:
Der Vaterklasse, aus der sie abgeleitet wurde (bzw. der Klasse Object, falls
keine extends-Klausel vorhanden war).
Den n Interfaces, die sie implementiert.
Wir könnten beispielsweise vorhin definierte Klasse Auto erweitern und sie die
Interfaces Groesse und Comparable implementieren lassen:
HTBLA Kaindorf/Sulm
Seite 6 von 13
PUC | Interfaces und deren Anwendung
Grundlagen
1 public class Auto2 implements Groesse, Comparable {
2
public String name;
3
public int
erstzulassung;
4
public int
leistung;
5
public int
laenge;
6
public int
hoehe;
7
public int
breite;
8
9
public int laenge() {
10
return this.laenge;
11
}
12
13
public int hoehe() {
14
return this.hoehe;
15
}
16
17
public int breite() {
18
return this.breite;
19
}
20
21
public int compareTo(Object o) {
22
int ret = 0;
23
if (leistung < ((Auto2)o).leistung) {
24
ret = -1;
25
} else if (leistung > ((Auto2)o).leistung) {
26
ret = 1;
27
}
28
return ret;
29
}
30 }
Auto2
«interface»
Groesse
+laenge()
+hoehe()
+breite()
«interface»
Comparable
name: String
leistung: int
laenge: int
hoehe: int
breite: int
+laenge()
+hoehe()
+breite()
+compareTo(Object)
+compareTo(Object)
Abbildung 4: UML-Notation der Klasse Auto2
Nun sind Objekte dieses Typs sowohl zu Groesse als auch zu Comparable kompatibel (Hinweis: die Sortierung basiert in diesem Fall nicht auf der Größe des
Autos, sondern auf seiner Leistung).
1.5
Vererbung von Interfaces
Die Implementierung hätte noch etwas vereinfacht werden können, wenn wir
uns zu Nutze gemacht hätten, dass eine Klasse die Interfaces seiner Basisklasse erbt:
HTBLA Kaindorf/Sulm
Seite 7 von 13
PUC | Interfaces und deren Anwendung
Grundlagen
1 public class Auto3 extends Auto
2
implements Comparable {
3
4
public int compareTo(Object o) {
5
int ret = 0;
6
if (leistung < ((Auto3)o).leistung) {
7
ret = -1;
8
} else if (leistung > ((Auto3)o).leistung) {
9
ret = 1;
10
}
11
return ret;
12
}
13 }
Auto3 erbt von Auto nicht nur die Implementierung, sondern auch die Klausel
implements Groesse und erweitert sie um die Implementierung von Comparable. Damit ist Auto3 gleichwertig zu Auto2.
1.6
Ableiten von Interfaces
Auch Interfaces selbst können abgeleitet werden. Ähnlich einer Klasse erbt das
abgeleitete Interface alle Methodendefinitionen des Basis-Interfaces. Die implementierende Klasse muss also auch alle Methoden von allen übergeordneten
Interfaces implementieren:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
interface EinDimensional {
public int laenge();
}
interface ZweiDimensional extends EinDimensional {
public int breite();
}
interface DreiDimensional extends ZweiDimensional {
public int hoehe();
}
interface VierDimensional extends DreiDimensional {
public int lebensdauer();
}
public class InterfaceDemo2 implements VierDimensional {
public int laenge() { return 0; }
public int breite() { return 0; }
public int hoehe() { return 0; }
public int lebensdauer() { return 0; }
}
HTBLA Kaindorf/Sulm
Seite 8 von 13
PUC | Interfaces und deren Anwendung
Anwendung von Interfaces
2
Anwendung von Interfaces
2.1
Konstanten in Interfaces
Neben abstrakten Methoden können Interfaces auch Konstanten, also Membervariablen mit den Attributen static und final, enthalten. Wenn eine Klasse
ein Interface implementiert, erbt es auch alle Konstanten. Es ist auch erlaubt,
dass ein Interface ausschließlich Konstanten enthält.
Dieses Feature kann zum Beispiel nützlich sein, wenn ein Programm sehr viele
Konstanten definiert. Anstatt sie in ihren eigenen Klassen zu belassen und bei
jedem Gebrauch mit Klasse.Name aufzurufen, könnte ein einzelnes Interface
definiert werden, das alle Konstantendefinitionen vereinigt. Wenn nun jede
Klasse, in der diese Konstanten benötigt werden, dieses Interface implementiert, stehen alle darin definierten Konstanten direkt zur Verfügung und können
ohne vorangestellten Klassennamen aufgerufen werden.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2.2
interface Debug {
public static
public static
public static
public static
}
final
final
final
final
boolean
boolean
boolean
boolean
FUNCTIONALITY1
FUNCTIONALITY2
FUNCTIONALITY3
FUNCTIONALITY4
=
=
=
=
false;
true;
false;
false;
public class InterfaceDemo3 implements Debug {
public static void main(String[] args) {
//...
if (FUNCTIONALITY1) {
System.out.println("...");
}
//...
if (FUNCTIONALITY2) {
System.out.println("...");
}
//...
}
}
Implementierung von Flags
Einige Interfaces definieren weder Methoden noch Konstanten. Sie stellen statt
dessen eine Art Flag, also einen logischen Schalter dar, der zur Compile- und
Laufzeit abgefragt werden kann. Beispiele aus der Klassenbibliothek sind die
Interfaces java.io.Serializable oder java.lang.Cloneable. Wir wollen uns
nachfolgend die Bedeutung des Interfaces Cloneable ansehen.
HTBLA Kaindorf/Sulm
Seite 9 von 13
PUC | Interfaces und deren Anwendung
Anwendung von Interfaces
Cloneable ist ein Schalter für die in der Klasse Object implementierte Methode
clone(). Implementiert eine abgeleitete Klasse dieses Interface nicht, so deutet clone() das als fehlende Fähigkeit (oder Bereitschaft) der Klasse, eine Objektkopie herzustellen, und löst beim Aufruf eine CloneNotSupportedException aus. Implementiert die abgeleitete Klasse dagegen Cloneable, erzeugt
ein Aufruf von clone() eine elementweise Kopie des aktuellen Objekts.
Es ist wichtig zu verstehen, was der Begriff elementweise bedeutet – insbesondere wenn die Klasse Objekte als Membervariablen enthält. Beim Aufruf von
clone() werden nämlich lediglich die Verweise auf diese Membervariablen kopiert, nicht aber die dahinter stehenden Objekte (bei primitiven Membervariablen macht das keinen Unterschied, denn sie werden nicht als Zeiger gespeichert). Diese Vorgehensweise wird auch als shallow copy bezeichnet ("flache
Kopie").
Soll eine deep copy ("tiefe Kopie") angelegt werden, muss man clone() überlagern und selbst dafür sorgen, daß alle vorhandenen Objekt-Membervariablen
kopiert werden. Da jedes Memberobjekt weitere Objekte enthalten kann, die
kopiert werden müssen, ist das Erstellen einer tiefen Kopie in der Regel ein rekursiver Vorgang.
2.3
Nachbildung von Funktionszeigern
Eine wichtige Anwendung von Interfaces besteht darin, die aus C oder C++ bekannten, aber in Java nicht vorhandenen Funktionszeiger nachzubilden, die es
ermöglichen, eine Funktion als Argument an andere Funktionen zu übergeben.
Nützlich ist das vor allem, wenn die Konfigurationsanforderungen einer Funktion die durch die Übergabe von Variablen gegebenen Möglichkeiten übersteigen.
Beispiele für ihre Anwendung sind etwa Funktionsplotter oder CallbackFunktionen bei der Programmierung grafischer Oberflächen.
Funktionszeiger können leicht mit Hilfe von Interfaces nachgebildet werden.
Dazu wird zunächst ein Interface definiert, das eine einzelne Methode f des
gewünschten Typs deklariert. Es kann dann von unterschiedlichen Klassen so
implementiert werden, dass in f jeweils die gewünschte Berechnung ausgeführt
wird. Anstelle der Übergabe eines Zeigers wird nun einfach ein Objekt dieser
Klasse instanziert und an die zu konfigurierende Methode übergeben. Diese
wird vom Typ des Interfaces deklariert und kann so die Methode f aufrufen.
Als Beispiel soll ein Programm geschrieben werden, das in der Lage ist, eine
Wertetabelle für beliebige double-Funktionen auszugeben. Wir definieren dazu
zunächst ein Interface DoubleMethod, das eine Methode compute deklariert, die
zu einem double-Argument ein double-Ergebnis berechnet.
HTBLA Kaindorf/Sulm
Seite 10 von 13
PUC | Interfaces und deren Anwendung
Anwendung von Interfaces
1 public interface DoubleMethod {
2
public doubel compute(double value);
3 }
Anschließend implementieren wir das Interface in verschiedenen Klassen und
stellen die Funktionen Exponentation, Quadratwurzel, Multiplikation mit zwei
und Quadrat zur Verfügung. Anschließend instanzieren wir diese Klassen und
übergeben die Objekte nacheinander an die Methode printTable, mit der die
Wertetabelle erzeugt und ausgegeben wird:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Exp implements DoubleMethod {
public double compute(double value) {
return Math.exp(value);
}
}
class Sqrt implements DoubleMethod {
public double compute(double value) {
return Math.sqrt(value);
}
}
class Times2 implements DoubleMethod {
public double compute(double value) {
return 2 * value;
}
}
class Sqr implements DoubleMethod {
public double compute(double value) {
return value * value;
}
}
public class InterfaceDemo4 {
public static void printTable(DoubleMethod meth) {
System.out.println("Wertetabelle " + meth.toString());
for (double x = 0.0; x <= 5.0; x += 1) {
System.out.println(x + "->" + meth.compute(x));
}
}
public static void
printTable(new
printTable(new
printTable(new
printTable(new
}
main(String[] args) {
Times2());
Exp());
Sqr());
Sqrt());
}
HTBLA Kaindorf/Sulm
Seite 11 von 13
PUC | Interfaces und deren Anwendung
Folienvorschlag
Folienvorschlag
Grundlagen
Definition
Implementierung
Verwendung
Mehrfachimplementierung
Vererbung
Ableitung
Anwendung
Konstanten
Implementierung von Flags
Nachbildung von Funktionszeigern
HTBLA Kaindorf/Sulm
Seite 12 von 13
PUC | Interfaces und deren Anwendung
Zusammenfassung
Zusammenfassung
In diesem Skriptum wurden folgende Themen behandelt:
Definition, Implementierung und Verwendung von Interfaces.
Implementierung mehrer Interfaces.
Vererbung von Interfaces.
Ableitung von Interfaces.
Verwendung als Konstanten-Interface.
Verwendung als Flag-Interface.
Tiefe und flache Kopie und die Methode clone().
Nachbildung von Funktionszeigern.
Quellen
Handbuch der Java-Programmierung, 3. Auflage (http://www.javabuch.de)
Java 2 – Designmuster (http://www.galileocomputing.de)
Java ist auch eine Insel, 2. Auflage (http://www.galileocomputing.de)
Sun Microsystems, Java Technologie Home Page (http://java.sun.com)
HTBLA Kaindorf/Sulm
Seite 13 von 13
Herunterladen