From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001 From: Sven Eisenhauer Date: Fri, 10 Nov 2023 15:11:48 +0100 Subject: add new repo --- .../hjp5/html/k100061.html | 499 +++++++++++++++++++++ 1 file changed, 499 insertions(+) create mode 100644 Master/Reference Architectures and Patterns/hjp5/html/k100061.html (limited to 'Master/Reference Architectures and Patterns/hjp5/html/k100061.html') diff --git a/Master/Reference Architectures and Patterns/hjp5/html/k100061.html b/Master/Reference Architectures and Patterns/hjp5/html/k100061.html new file mode 100644 index 0000000..af4079c --- /dev/null +++ b/Master/Reference Architectures and Patterns/hjp5/html/k100061.html @@ -0,0 +1,499 @@ + + + +Handbuch der Java-Programmierung, 5. Auflage + + + + + + + + + +
 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

+ +

+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: + + +

+ + + + + +
+ +
+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
+ +Listing 9.11: Konstanten in Interfaces

+ + + + +

static import

+ +

+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.

+ + + + +
 JDK1.1-6.0 
+
+ +

+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
+ +Listing 9.12: static import

+ + + + +

9.4.2 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. +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
+ +Listing 9.13: Implementierung einer tiefen Kopie

+ + + + +

9.4.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 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
+ +Listing 9.14: Das Interface DoubleMethod

+ +

+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
+ +Listing 9.15: Funktionszeiger mit Interfaces

+

+ + + + + + + + + +
+ +

+In Abschnitt 43.3.2 wird +gezeigt, wie man Funktionszeiger auf ähnliche Weise mit dem Reflection-API +realisieren kann.

+ + + + +
 Hinweis 
+
+


+ + + +
 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 +
+ + + -- cgit v1.2.3