|
|||||||||||||||||||||||||||||||||||||||||
| ISBN: 3897214482 ISBN: 3897214482 ISBN: 3897214482 ISBN: 3897214482 | |||||||||||||||||||||||||||||||||||||||||
|
Wir empfehlen: | ||||||||||||||||||||||||||||||||||||||||
3.
Programmierung für Vererbung 3.1.
Motivation Die Motivation für Vererbung zu programmieren liegt in der bestmöglichsten Wiederverwendbarkeit des Source-Codes. Wie in Abschnitt 2.3 gesehen, bestehen zwei Möglichkeiten Code wiederzuverwenden. Diese beide Möglichkeiten sind leicht anzuwendende Verfahren, mit denen man erreicht, den "alten" Quellcode anzupassen und zu erweitern. Trotz der durch Java relativ geschützten Typsicherheit, erhöht sich jedoch die Komplexität des Programms. Um an dieser Stelle Fehler zu vermeiden muss der Entwickler oder Verwender sicherstellen, dass keine Seiteneffekte oder unerwünschte Zugriffe auf die Klassen, bzw. deren Mitglieder stattfinden. Deshalb ist es notwendig bereits im voraus zu planen. Das Programm sollte durch sinnvolle Klassen und Methoden strukturiert werden. Zunächst ist es sinnvoll, den Programmcode in kleine Methoden nach ihrer Funktionsweise aufzugliedern, um gezielt einzelne Bereiche anpassen zu können und sich nicht durch zu frühe Spezialisierung einzuschränken. Man erreicht eine wesentlich flexiblere Vererbung, jedoch erschwert dies auch die Leserlichkeit des Source-Codes. Ein weiterer Aspekt ist das Schachteln von Methoden, speziell das Aufrufen von Methoden innerhalb von Konstruktoren, was aufgrund der dynamischen Methodenbindung zu fatalen Seiteneffekten führen kann: class SuperKlasse {Beispiel dynbind.java Wenn man nun eine Instanz von SubKlasse erzeugt, wird implizit der Konstruktor der SuperKlasse aufgerufen. Dort wird die SubKlassenVariable a initialisiert und die Methode printMe() aufgerufen. Durch die dynamische Bindung wird jedoch die SubKlassen-Methode aufgerufen, was zu einem Programmabbruch durch eine NullPointerException führt, da das Attribut b der SubKlasse noch nicht initialisiert wurde. Weitere Gefahren entstehen bei unzureichendem Zugriffschutz. Auf Zugriffschutz durch geeignete Anwendung von Kapselungstechniken wird im Abschnitt 2.4. genauer eingegangen. 3.2. Subclassing / Abstrakte Klassen / Interfaces / Substituierbarkeit Im vorhergehenden Abschnitt wurde der Begriff Typsicherheit erwähnt. Dieser Begriff geht einher mit Vererbung und Subtyping. Für Subtyping ist grundsätzlich verlangt, dass Subtyp-Objekte alle Methoden vom Supertyp unterstützen und insbesondere auch besitzen. Weiterhin ist auch verlangt, dass die Methoden die gleichen Parameter, sowie gleichen Rückgabetyp haben. Diese syntaktische Bedingung ist auch bei den von Java unterstützten Interface zu finden (class SubTyp implements InterfaceA { ... }). Diese Bedingung ist ebenfalls durch die Vererbung in Java erfüllt. Bei den meisten OOP-Sprachen, insbesondere Java, fällt die Vererbungshierarchie (vor allem wegen der fehlenden Mehrfachvererbung, vgl. Abschnitt 3.3.) direkt mit der SubTyp-Hierarchie zusammen. Deshalb spricht man bei Vererbung wegen der Verbindung zur Subtypisierung von Subclassing. In dem Moment wo eine Klasse von einer anderen erbt besitzt sie ihren Typ. So ist es zum Beispiel in Java nicht möglich, dass eine Klasse durch mehrfache Vererbung mehrere Typen in sich vereinigt. Um aber einen gemeinsamen Typen festzulegen kann man sich in Java der Interfaces und der abstrakten Klassen bedienen. Das Ziel besteht im Zusammenfassen gemeinsamer Implementierungsteile zukünftiger Subtypen an einer Stelle. Abstrakte Klassen sind ausschließlich vererbbar, d.h. man kann sie nur durch extends weitervererben und somit ihre SubKlasse mit dem Typ der abstrakten Klasse ausstatten und die SubKlasse muss alle in der abstrakten Klassen definierten Methoden deklarieren. Eine abstrakte Klasse kann auch Teilimplementierungen, sowie Konstanten enthalten. Interfaces sind gegenüber den abstrakten Klassen im Vergleich nicht vererbbar, sondern nur mit implements implementierbar. In Java dürfen jedoch beliebig viele Interfaces in einer Klasse implementiert werden, jedoch nur eine Klasse vererbt. Weiterhin besitzen Interfaces nur Operationssignaturen und keine Attribute. Abstrakte Klassen stellen wie die SuperKlassen, von denen man erben kann, einen Obertypen zu dem entstehenden Subtypen dar und es werden auf dem selben Pfad in der Vererbungshierarchie nach unten laufend alle direkten und indirekten Obertypen weitervererbt. abstract class SampleAbstract {Die abstrakte Klasse SampleAbstract stellt eine Methode und ein Attribut zur Verfügung, die vererbt werden können. In diesem Beispiel wird angenommen, dass die Methode in anderen Klassen gleich verwendet wird. Die abstrakte Methode move() enthält keine Gemeinsamkeiten in der Implementierung und muss deshalb für jede Klasse eigens implementiert werden: interface SampleInterface {Das Interface SampleInterface stellt zwei Methodensignaturen zur Verfügung, die implementiert werden müssen, falls eine Klasse mit dem Interface erweitert wird. Die Klasse Sample implementiert das Interface SampleInterface und erbt von SampleAbstract: class Sample extends SampleAbstract implements SampleInterface {Beispiel abstrakt.java Das auffällige an diesem Beispiel ist, dass die Methoden copy() und move() aufgrund der Implementierungspflicht implementiert wurden, jedoch die Methode print() nicht, da eine Implementierung bereits samt dem Attribut a geerbt wurde und somit nicht mehr zu implementieren ist. Ein Objekt der Klasse Sample hätte jetzt die Typen SampleInterface und SampleAbstract. Ein weiterer Aspekt der aus SubClassing resultiert ist die Substituierbarkeit (Substitutability). Unter Substituierbarkeit versteht man das mögliche Einsetzen eines SubTyp an der Stelle wo eigentlich der SuperTyp des SubTyps erwartet wird. Es ist allerdings nicht möglich, dass man einen SuperTyp dort einsetzt (substituiert), wo ein SubTyp erwartet wird. Dies ist möglich, da durch die Vererbung an den SubTypen, der SubTyp genau die selben, eventuell verfeinerten, Methoden und Attribute besitzt. Allgemein ausgedrückt, ist eine Substituierung in Richtung der Generalisierung grundsätzlich möglich. Aus dem obigen Beispiel hat sich ergeben, dass ein Sample-Objekt zwei Typen hat. Dies führt direkt auf die Frage Mehrfachvererbung. 3.3. Mehrfachvererbung Wie bereits mehrfach erwähnt besitzt Java nicht die Möglichkeit direkt mehrere Klassen an eine Klasse zu vererben. Jedoch kann Mehrfachvererbung (multiple inheritance), genauer gesagt nur die Mehrgestaltigkeit (Polymorphismus), durch Einfachvererbung (single inheritance) und mehrfachen Subtyping über Schnittstellen erzeugt werden, wie man es im obigen Beispiel sehen kann. In der Programmiersprache C++ ist es jedoch möglich mehrfach zu vererben: class Reptil{Die Klasse Amphibium erbt von den Klassen Reptil und Fisch mit ihren Attributen, Methoden und Implementierungen. Durch Mehrfachvererbung entstehen jedoch Nachteile entstehend aus der resultierenden Unübersichtlichkeit und der durch Seiteneffekte entstehenden Fehlerträchtigkeit. Da die Methoden dynamisch zur Laufzeit gebunden werden, kann man von vorne herein nicht sagen, ob Fisch.move() oder Reptil.move() gebunden wird. Dies muss vorher festgelegt werden. Jedoch bleibt auch unklar wie oft ein Attribut vererbt wurde und welches man verwenden sollte. An diesem Punkt stellt sich jetzt die Frage, ob und wenn ja wie man bestimmte Implementierungen, speziell bei der Einfachvererbung in Java, sperren bzw. verstecken kann. 3.4.
Kapselung Die vorherigen Abschnitte haben erläutert in welcher Weise man vererben kann und welche Schwierigkeiten sich dadurch ergeben können. Das Ziel was man aber ins Auge fasst ist die optimale Wiederverwendbarkeit. Aufgrund dessen sollte der Anwender von Klassen nicht die Möglichkeit haben sie zu verändern, bzw. auch nicht die Möglichkeit haben sie einzusehen, da ein Anwender meist nicht mit Details der Implementierung vertraut ist und so mögliche falsche Rückschlüsse ziehen kann und den Programm-Code verändert oder durch Unwissenheit falsch benutzt. Um dies zu vermeiden und die Möglichkeit zu haben das Klassenmodell transparent zu gestalten verfügt Java, ebenso wie C++ und andere OOP-Sprachen, über Kapselungskonstrukte, die es ermöglichen bestimmte Teile einer Klasse zu verstecken bzw. zu sperren. Die allgemeinen Zugriffmodifikatoren (access modifier) der Kapselung über Methoden, Attribute und Konstruktoren, nämlich private, für den privaten Zugriff auf Methoden und Attribute nur innerhalb der Klasse, package (default-Einstellung), der nur innerhalb eines Pakets den Zugriff erlaubt und public, der einen öffentlichen Zugriff gestattet, werden für die Vererbung in Java durch weitere Zugriffmodifikatoren erweitert. Durch die dynamische Methodenbindung wird die Prüfung der Zugriffregeln erschwert, da zur Übersetzungszeit nicht bekannt ist, welche Methode aufgerufen wird. Um die Zugriffbeschränkungen trotzdem statisch prüfen zu können, müssen überschreibende, bzw. implementierende Methoden (die der SubKlasse) die selben Zugriffrechte besitzen, wie die Überschriebene, Verfeinerte (also die der SuperKlasse). Folgendes Beispiel einer Queue mit einem Iterator dient zur Veranschaulichung des protected-Modifiers und als Beispiel für die Kapselung von Objektgeflechten in Abschnitt 4: class Queue {//QueueIterator - inner class protected
- geschützter Zugriff Es bestehen grundsätzlich zwei Möglichkeiten die obige Implementierung einer Queue zu verwenden. Wenn die Queue der reinen Anwendung dient, dann hätte es ausgereicht, alle derzeit mit protected geschützten mit private zu deklarieren. So hätte der Anwender z. B. keinen Zugriff auf das head-Element der Queue gehabt, um die Datenstruktur zu zerstören. Möchte der Anwender oder Programmierer jedoch die Queue ausbauen, sprich er erweitert seine Klasse mit der bestehenden Queue, so hätte er mit einem reinen private-Zugriffschutzmechanismus keine Möglichkeit mehr in der erbenden Klasse auf diese Elemente zugreifen zu können. Im Falle der Vererbungsnutzung stellt Java den Zugriffmodifikator protected zur Verfügung, der den klasseninternen Zugriff auf diese geschützten Elemente auch noch in der erbenden Klasse zulässt, aber auch dort nach außen die Elemente nicht sichtbar macht. Die Elemente der SubKlasse haben somit private-Zugriff auf die geerbten Programmteile. final - gesperrte Vererbung Beispiel calc.java In der Klasse calculate befindet sich die Methode setA(int i), welche die Membervariable a den Wert des Parameters i zuweist, falls dieser größer 0 ist. Durch überschreiben von Methoden beim Vererben ist es jedoch möglich, dass man die Eigenschaft einer Klasse völlig verändert, insbesondere auch außerhalb des Pakets, in dem der Klassentyp deklariert wurde, falls ein Paket importiert wurde. Durch das Überschreiben kann man eine SubKlasse erzeugen, die die Funktionsweise der SuperKlasse nicht mehr erfüllt, wie im obigen Beispiel zu sehen ist. Dort wurde die Methode setA(int i) durch eine ähnliche Methode ersetzt, die aber im Gegensatz zur Überschriebenen keine Prüfung vornimmt, ob der übergebene int-Wert 0 ist. Dies scheint auf den ersten Blick den Programmablauf nicht zu beeinflussen. Ruft man jedoch die Methode divide() auf, und die Member-Variable a wurde von ihrem default-Wert 1, durch die überschreibende Methode auf den Wert 0 gesetzt führt dies zu einer arithmetischen Ausnahmebehandlung und das Programm terminiert mit einem Fehler. In derartigen Situationen ist es von Nöten, den Vererbungsmechanismus noch weiter einzuschränken. Java bietet hierfür anstelle des protected-Modifiers den final-Modifier, welcher Methoden und Attribute als unveränderlich, nicht überschreibbar, deklariert. Von unveränderlichen Klassen können keine SubKlassen gebildet werden! (vgl. class String) Solche Klassen, Methoden und Attribute heißen "value object" oder "immutable objects". 3.5. Zusammenspiel Kapselung und Vererbung Ein noch ungeklärter Punkt sind als private deklarierte Methoden bei der Vererbung. Zuvor wurde festgestellt, dass private-Methoden in der erbenden Klasse nicht zur Verfügung stehen. Es stellt sich die Frage, was ist aber mit Methoden, die vererbt wurden und für ein Funktionieren diese private-Methoden benötigen? Mit private deklarierte Methoden und Attribute sind in SubKlassen nicht zugreifbar, sie stehen aber den vererbten Methoden zur Verfügung, um korrekt funktionieren zu können. Wird in der SubKlasse ein private Methode (mit gleicher Signatur) überschrieben, dann werden die Methoden ("geerbte" Methode und "überschriebene" Methode) getrennt behandelt, als hätten sie unterschiedliche Namen oder Signatur, d.h. es findet bei vererbten private-Methoden keine dynamische Methodenauswahl statt. Aufgrund dieser fehlenden dynamischen Bindung kann eine Kapselung, bei unachtsamer Anwendung, Auswirkung auf die Laufzeitsemantik des Programms haben: //class represents pair of intBeispiel PrivateTest.java In der mit (x) markierten Zeile wird die Methode sum() neu geschrieben. Wäre dies nicht der Fall, dann würde das Objekt it nur die ersten beiden Werte addieren, da die ererbte Methode sum() aus der SuperKlasse aufgerufen und dort dynamisch an die add()-Methode der SuperKlasse gebunden wird, wegen dem this.add()-Aufruf. Eine weitere Gefahr stellt, vor allem beim Zusammenspiel mit Vererbung und Kapselung, der super.sum()-Aufruf der mit (+) markierten Zeile dar, welcher einen Aufruf der Methode sum() der IntPair-Klasse (*) nach sich zieht und dort einen this.add()-Aufruf auswertet. Dabei referenziert der this-Parameter ein IntTriple-Objekt, aber es wird trotzdem die Methode der IntPair-Klasse ausgeführt, da die add()-Methode aufgrund der private-Deklaration, wie oben erklärt, nicht überschrieben wurde, insbesondere nicht von der add()-Methode aus der SubKlasse, trotz der identischen Signatur. Hätte man die add-Methoden nicht private deklariert, dann wäre der add()-Aufruf in Zeile (*) dynamisch gebunden worden und hätte auf ein IntTriple-Objekt referenziert, was zu einer nicht terminierenden Rekursion geführt hätte. |
|
||||||||||||||||||||||||||||||||||||||||
| |<< First < Previous Index Next > Last >>| | |||||||||||||||||||||||||||||||||||||||||
|
Back to the topic site: StudyPaper.com/Startseite/Computer/Informatik/Programmieren/Java External Links to this site are permitted without prior consent. | |||||||||||||||||||||||||||||||||||||||||
| Home | deutsch | Set bookmark | Send a friend a link | Copyright © | Impressum | |||||||||||||||||||||||||||||||||||||||||