From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001
From: Sven Eisenhauer
+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 seine 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 durch den qualifizierten Ausdruck
+Klasse.Name aufzurufen, könnte
+ein 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 qualifizierten Klassennamen
+aufgerufen werden.
+
+
+Das folgende Listing zeigt die Anwendung dieses Prinzips am Beispiel
+eines Interface, das eine Reihe von Debug-Konstanten zur Verfügung
+stellt. Sie werden beispielsweise zur bedingten Übersetzung oder
+zur Steuerung von Debug-Ausgaben verwendet:
+
+
+
+
+
+
+ Titel
+ Inhalt
+ Suchen
+ Index
+ DOC
+ Handbuch der Java-Programmierung, 5. Auflage
+
+ <<
+ <
+ >
+ >>
+ API
+ Kapitel 9 - OOP III: Interfaces
+
+
+
+
+
+9.4 Weitere Anwendungen von Interfaces
+
+
+
+
+
+
+
+9.4.1 Konstanten in Interfaces
+
+
+
+
+Listing 9.11: Konstanten in Interfaces
+
+
+
+
+
+001 /* Listing0911.java */
+002
+003 interface Debug
+004 {
+005 public static final boolean FUNCTIONALITY1 = false;
+006 public static final boolean FUNCTIONALITY2 = true;
+007 public static final boolean FUNCTIONALITY3 = false;
+008 public static final boolean FUNCTIONALITY4 = false;
+009 }
+010
+011 public class Listing0911
+012 implements Debug
+013 {
+014 public static void main(String[] args)
+015 {
+016 //...
+017 if (FUNCTIONALITY1) {
+018 System.out.println("...");
+019 }
+020 //...
+021 if (FUNCTIONALITY2) {
+022 System.out.println("...");
+023 }
+024 //...
+025 }
+026 }
+
+
+Listing0911.java
+
+Durch die implements-Anweisung +können die Konstanten direkt, d.h. ohne vorangestellten Interface-Namen +verwendet werden. Diese praktische und von vielen Entwicklern verwendete +Möglichkeit ist allerdings nicht unbedingt im Sinne des Erfinders. +Die Implementierung eines Interface soll ja schließlich die +Zugehörigkeit einer Klasse zu einem bestimmten Typ dokumentieren +- nicht die Schreibfaulheit des Entwicklers. +
+
![]() |
+![]() |
+
+
+ +Seit der J2SE 5.0 gibt es deshalb eine Möglichkeit, statische +Teile aus Klassen und Interfaces unqualifiziert zu benutzen, ohne +implements +zu verwenden. Bei diesem, als static import bezeichneten Verfahren +werden die benötigten statischen Bestandteile in einer speziellen +import-Anweisung +aufgelistet, bei der nach dem Schlüsselwort import static +der qualifizierte Name des statischen Members folgt. Dieser kann dann +im weiteren Verlauf der Klasse unqualifiziert verwendet werden. Sollen +alle statischen Member auf einmal verfügbar gemacht werden, ist +Klasse.* zu schreiben. Dieses +Verfahren funktioniert sowohl mit Interfaces als auch mit Klassen +und kann sowohl Methoden wie auch Membervariablen importieren. |
+
+
|
+![]() |
+
+Das folgende Beispiel zeigt die Anwendung dieses neuen Features am +Beispiel der Klasse java.lang.Math. +Die Methoden sqrt +und sin +sowie die Konstante PI +können nach dem statischen Import der Klasse unqualifiziert verwendet +werden: + + +
+
+
+
+001 /* Listing0912.java */
+002
+003 import static java.lang.Math.*;
+004
+005 public class Listing0912
+006 {
+007 public static void main(String[] args)
+008 {
+009 System.out.println(sqrt(2));
+010 System.out.println(sin(PI));
+011 }
+012 }
+
+ |
++Listing0912.java | +
+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. +Während Serializable +in Kapitel 41 ausführlich +gewürdigt wird, wollen wir uns nachfolgend die Bedeutung des +Interfaces Cloneable +ansehen. + +
+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, dass 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. + +
+Wir wollen uns beispielhaft das tiefe Kopieren der folgenden Klasse +BinTreeNode ansehen. Diese stellt +einen Knoten in einem Binärbaum dar und besitzt einen Namen und +die üblichen Verweise auf den linken und rechten Unterbaum, ebenfalls +vom Typ BinTreeNode. Beim Kopieren +wird das aktuelle Objekt durch Aufruf von super.clone +zunächst flach kopiert. Dann wird clone +rekursiv aufgerufen, um Kopien des linken bzw. rechten Unterbaums +anzulegen. Enthält ein Unterbaum den Wert null, +so terminiert der Kopiervorgang und mit ihm die Rekursion. Auf diese +Weise wird der Knoten mit allen Unterknoten, Unterunterknoten usw. +kopiert und es entsteht eine Kopie, deren Objektvariablen keine gemeinsamen +Objekte mit dem Original mehr besitzen. + + +
+
+
+
+001 /* BinTreeNode.java */
+002
+003 class BinTreeNode
+004 implements Cloneable
+005 {
+006 String name;
+007 BinTreeNode leftChild;
+008 BinTreeNode rightChild;
+009
+010 public BinTreeNode(String name)
+011 {
+012 this.name = name;
+013 this.leftChild = null;
+014 this.rightChild = null;
+015 }
+016
+017 public Object clone()
+018 {
+019 try {
+020 BinTreeNode newNode = (BinTreeNode)super.clone();
+021 if (this.leftChild != null) {
+022 newNode.leftChild = (BinTreeNode)this.leftChild.clone();
+023 }
+024 if (this.rightChild != null) {
+025 newNode.rightChild = (BinTreeNode)this.rightChild.clone();
+026 }
+027 return newNode;
+028 } catch (CloneNotSupportedException e) {
+029 //Kann eigentlich nicht auftreten...
+030 throw new InternalError();
+031 }
+032 }
+033 }
+
+ |
++BinTreeNode.java | +
+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 Callback-Funktionen 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. + + +
+
+
+
+001 /* DoubleMethod.java */
+002
+003 public interface DoubleMethod
+004 {
+005 public double compute(double value);
+006 }
+
+ |
++DoubleMethod.java | +
+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: + + +
+
+
+
+001 /* Listing0915.java */
+002
+003 class MathExp
+004 implements DoubleMethod
+005 {
+006 public double compute(double value)
+007 {
+008 return Math.exp(value);
+009 }
+010 }
+011
+012 class MathSqrt
+013 implements DoubleMethod
+014 {
+015 public double compute(double value)
+016 {
+017 return Math.sqrt(value);
+018 }
+019 }
+020
+021 class Times2
+022 implements DoubleMethod
+023 {
+024 public double compute(double value)
+025 {
+026 return 2 * value;
+027 }
+028 }
+029
+030 class Sqr
+031 implements DoubleMethod
+032 {
+033 public double compute(double value)
+034 {
+035 return value * value;
+036 }
+037 }
+038
+039 public class Listing0915
+040 {
+041 public static void printTable(DoubleMethod meth)
+042 {
+043 System.out.println("Wertetabelle " + meth.toString());
+044 for (double x = 0.0; x <= 5.0; x += 1) {
+045 System.out.println(" " + x + "->" + meth.compute(x));
+046 }
+047 }
+048
+049 public static void main(String[] args)
+050 {
+051 printTable(new Times2());
+052 printTable(new MathExp());
+053 printTable(new Sqr());
+054 printTable(new MathSqrt());
+055 }
+056 }
+
+ |
++Listing0915.java | +
+
![]() |
+
+
+ +In Abschnitt 43.3.2 wird +gezeigt, wie man Funktionszeiger auf ähnliche Weise mit dem Reflection-API +realisieren kann. |
+
+
|
+![]() |
+
| Titel + | Inhalt + | Suchen + | Index + | DOC + | Handbuch der Java-Programmierung, 5. Auflage, Addison +Wesley, Version 5.0.1 + |
| << + | < + | > + | >> + | API + | © 1998, 2007 Guido Krüger & Thomas +Stark, http://www.javabuch.de + |