Thomas W Zusammenfassung Objektorientierte Programmierung WS 06/07. Zusammenfassung. Objektorientierte Programmierung WS 06/07.

Ähnliche Dokumente
Kapitel 1 - Widerholungsfragen

Das Ersetzbarkeitsprinzip

Arten von Klassen-Beziehungen

1. In welchen Formen (mindestens zwei) kann man durch das Ersetzbarkeitsprinzip Wiederverwendung erzielen?

Arten von Klassen-Beziehungen

Faustregeln zu Zusicherungen

Untertypen, Vererbung, Sichtbarkeit A01 OOP. Untertypen, Vererbung, Sichtbarkeit

Interaktionen zwischen Objekten durch Senden von Nachrichten und Reagieren auf empfangene Nachrichten

Ersetzbarkeit und Verhalten

Client-Server-Beziehungen

Client-Server Beziehungen

Ausarbeitung OOP. 1. Grundlagen und Ziele. 1.1 Konzepte objektorientierter Programmierung

Dynamische Typinformation A01 OOP. Dynamische Typinformation

Ersetzbarkeit, Client-Server Beziehungen

4. Vererbung. Idee der Vererbung. Wir wollen ein Verwaltungsprogramm für CDs und Videos entwickeln. Wir stellen uns dazu folgende Klassen vor:

Einstieg in die Informatik mit Java

Einstieg in die Informatik mit Java

Einführung in die Programmiersprache Java II

Objektorientierte Sprachen

Beispiel: Zwischen der Oberklasse und der abgeleiteten Klasse besteht eine ist ein Beziehung. Eine abgeleitete Klasse stellt eine Spezialisierung der

Einstieg in die Informatik mit Java

3 Objektorientierte Konzepte in Java

12 Abstrakte Klassen, finale Klassen und Interfaces

OOP. Tagesprogramm. Dynamische Typinformation. Homogene Übersetzung der Generizität. Generizität und Typumwandlungen

Ausnahmebehandlung in Java

Vererbung, Polymorphie

Software-Entwurfsmuster (weitere) A01 OOP. Software-Entwurfsmuster (weitere)

Objektorientierte Programmierung. Kapitel 22: Aufzählungstypen (Enumeration Types)

Java Einführung Vererbung und Polymorphie. Kapitel 13

Kapitel 2. Michal Domanski Seite 1

Institut für Programmierung und Reaktive Systeme. Java 6. Markus Reschke

Programmieren in Java

Vererbung. Gerd Bohlender. Institut für Angewandte und Numerische Mathematik. Vorlesung: Einstieg in die Informatik mit Java 23.5.

Kapitel 9. Programmierkurs. Attribute von Klassen, Methoden und Variablen. 9.1 Attribute von Klassen, Methoden und Variablen

Repetitorium Informatik (Java)

Universität Stuttgart Institut für Automatisierungstechnik und Softwaresysteme Prof. Dr.-Ing. M. Weyrich

Gebundene Typparameter

Algorithmen und Datenstrukturen

Objektorientierte Programmierung. Kapitel 12: Interfaces

Inhaltsüberblick. I. Grundbegriffe - Objekte und Klassen. Organisatorisches. I. Grundbegriffe - Objektorientierte Konzepte

Arten des universellen Polymorphismus

7. Schnittstellen Grundlagen zu Schnittstellen. 7. Schnittstellen

Einstieg in die Informatik mit Java

Weitere Beispiele. Beispiel CD-Spieler: Exemplare eines abstrakten Konzepts. 7. Schnittstellen. Schnittstelle: Syntax

1 Abstrakte Klassen, finale Klassen und Interfaces

Ersetzbarkeit und Verhalten

Innere Klassen. Gerd Bohlender. Institut für Angewandte und Numerische Mathematik. Vorlesung: Einstieg in die Informatik mit Java

185.A Software-Entwurfsmuster 1 OOP. Software-Entwurfsmuster

2.13 Vererbung. Rainer Feldmann Universität Paderborn Technische Informatik für Ingenieure (TIFI) WS 09/ Article

OOP und Angewandte Mathematik. Eine Einführung in die Anwendung objektorientierter Konzepte in der angewandten Mathematik

Kapitel 8. Programmierkurs. Methoden. 8.1 Methoden

Programmieren in Java

Grundzüge der Programmierung. Wiederverwendung VERERBUNG

Grundlagen der Fehlerbehandlung. Informatik B - Objektorientierte Programmierung in Java. Vorlesung 06: Ausnahme- und Fehlerbehandlung in Java.

Objektorientierte Programmierung Studiengang Medieninformatik

JAVA 5 Generics. Proseminar Programmiersprachen Thema Java 5 Generics 1

3 Objektorientierte Konzepte in Java

Prof. W. Henrich Seite 1

Javakurs FSS Lehrstuhl Stuckenschmidt. Tag 3 - Objektorientierung

Anwendungsentwicklung mit Java. Grundlagen der OOP, Vererbung, Schnittstellen, Polymorphie

Objektorientierte Programmierung Studiengang Medieninformatik

Klassenbeziehungen & Vererbung

1. Abstrakte Klassen

Client-Server-Beziehungen

Methoden und Wrapperklassen

Kapitel 9: Klassen und höhere Datentypen. Klassen und höhere. Objekte, Felder, Methoden. Küchlin/Weber: Einführung in die Informatik

TU München, Fakultät für Informatik Lehrstuhl III: Datenbanksysteme Prof. Alfons Kemper, Ph.D.

4. Vererbung Die Klasse Object. Die Klasse Object

Was sind Objekt, Klasse und Vererbung Identität, Zustand, Verhalten, Schnittstelle deklarierter, statischer und dynamischer Typ

Vererbung. Generalisierung und Spezialisierung Vererbung und Polymorphismus

Exceptions und Vererbung

14 Abstrakte Klassen, finale Klassen, Interfaces

TEIL I: OBJEKTORIENTIERUNG UND GRUNDKURS JAVA GRUNDLAGEN DER PROGRAMMIERUNG... 4

Klausur Grundlagen der Programmierung

14 Abstrakte Klassen, finale Klassen, Interfaces. Auswertung von Ausdrücken. Beispiel. Abstrakte Methoden und Klassen

Präsentation Interfaces

Programmieren II. Innere Klassen. Heusch 10, Ratz 5.2.1, Institut für Angewandte Informatik

Die Klasse java.lang.object. Thorsten Treffer

Grundlagen der Programmierung Prof. H. Mössenböck. 16. Ausnahmen (Exception Handling)

Neben der Verwendung von Klassen ist Vererbung ein wichtiges Merkmal objektorientierter

Probeklausur: Programmierung WS04/05

Java Vererbung. Inhalt

Arten des universellen Polymorphismus

Fragen zur OOP in Java

Überschreiben von Methoden

7. Objektorientierte Softwareentwicklung/3. Informatik II für Verkehrsingenieure

Sprechen Sie Java? Hanspeter Mössenböck. Tm\ dpunkt.verlag. Eine Einführung in das systematische Programmieren

! 1. Unterklassen und Vererbung! 2. Abstrakte Klassen und Interfaces! 3. Modularität und Pakete! 4. Ausnahmen (Exceptions) II.4.

Java Einführung Methoden in Klassen

Beispiele für Ausdrücke. Der imperative Kern. Der imperative Kern. Imperativer Kern - Kontrollstrukturen. Deklarationen mit Initialisierung

Einführung in die Programmierung für NF MI. Übung 07

Konzepte der Programmiersprachen

Themen der Übung. Methoden und Wrapperklassen. Vorteile von Methoden. Methoden. Grundlagen

Beispiel für überladene Methode

FH D. Objektorientierte Programmierung in Java FH D FH D. Prof. Dr. Ing. André Stuhlsatz. Wiederholung: Gerüstbeispiel. Vererbungshierarchie: Typ 0

Kapitel 8. Generische Klassen

Wiederholung zur Vorlesung Programmieren

Objektorientierte Programmierung. Kapitel 14: Interfaces

Transkript:

Zusammenfassung Objektorientierte Programmierung WS 06/07 Autor: Thomas W Hinweise Ich empfehle jedem, das Skriptum vor der mündlichen Prüfung durchgelesen und verstanden zu haben, und übernehme (natürlich) keine Verantwortung für falsche Inhalte. Ich habe es mir öfters durchgelesen und mir sind keine Fehler aufgefallen. Die Bilder die ich hier verwendet habe, sind aus dem offiziellen Skriptum der Lehrveranstaltung genommen. Ich hoffe, dass das kein Problem für die betroffenen darstellt. Falls doch: email an amtdklonf@gmx.net Viel Spaß beim Lernen für die Prüfung :) Seite 1 / 22

OOP Zusammenfassung Kapitel 1 Grundlagen und Ziele Objekte Ein Objekt ist eine grundlegende Einheit in der Ausführung eines Programms. Es kapselt Variablen und Routinen. Objekte sollten so weit wie möglich vom System abgegrenzt sein. Objekte werden häufig als black bzw grey-box verwendet. Man sieht den Großteil des Objekts also nicht von außen. Eigenschaften Identität Sie bezeichnet ein Objekt eindeutig. Man kann es über diese ansprechen. Man kann sie sich als die Speicherstelle vorstellen, an der das Objekt liegt. Zwei Objekte sind identisch, falls sie am selben Speicherplatz liegen. Zustand Der Zustand setzt sich aus den Werten der Variablen zusammen. Er ändert sich durch die Zuweisung von neuen Werten an eine bzw mehrere Variablen. Zwei Objekte sind gleich, falls sie denselben Zustand und dasselbe Verhalten haben. Objekte können gleich sein, wenn sich nicht identisch sind(sie sind dann Kopien voneinander). Verhalten Das Verhalten beschreibt wie sich das Objekt beim Erhalten einer Nachricht verhält. Es beschreibt also dessen Routinen(=Methoden). Das Verhalten ist vom Methodennamen und den Parametern abhängig. Implementierung Die Implementierung eines Objekts beschreibt es bis ins kleinste Detail(Implementierung der Eigenschaften und Methoden). Schnittstelle Beschreibt das Verhalten mit einem Detailiertheitsgrad der für Zugriffe von außen notwendig ist. Objekte implementieren ihre Schnittstellen. Dabei wird das Verhalten des Objekts genau beschrieben. Schnittstellen trennen also den Inhalt des Objekts von dessen verschiedenen Außenansichten. Data Hiding Die Details eines Objekts sind nach außen hin nicht bekannt, sondern nur die Art der Verwendung ist sichtbar. Data hiding zusammen mit Kapselung nennt man Datenabstraktion. Klassen Jedes Objekt gehört zu einer Klasse, welche dessen Implementierung genau beschreibt. Objekte sind Instanzen von Klassen. Instanzen einer Klasse haben dieselben Implementierungen und dieselben Schnittstellen. Als Programmierer erstellt man meistens Klassen, Objekte werden dann zur Laufzeit aus diesen erzeugt. Seite 2 / 22

Polymorphismus Polymorph bedeutet vielgestaltig. Eine Programmiersprache ist polymorph, wenn eine Variable oder Routine gleichzeitig mehrere Typen haben kann. Variablentypen Deklarierter Typ Die Variable wurde mit diesem Typ deklariert. Statischer Typ Dieser kann spezifischer sein, als der deklarierte Typ. Der Compiler ordnet einer Variablen an verschiedenen Stellen im Programm meistens verschiedene statische Typen zu. Man kann einen statischen Typ deshalb auch nicht deklarieren. Dynamischer Typ Ist der spezifischste Typ. den der in der Variable gespeicherte Wert tatsächlich hat. Diese sind dem Compiler nicht bekannt, da sie sich zur Laufzeit ändern können(zb durch Wertzuweisungen). Generizität Es wird die Gleichförmigkeit durch Typparameter erreicht. Das bedeutet, dass Ausdrücke Parameter enthalten können, für die man Typen einsetzt(zb List<String>). Universeller Polymorphismus sichert Generizität zu. Enthaltender Polymorphismus Diese Art, auch subtyping genannt, spielt in der objektorientierten Programmierung eine wichtige Rolle. Angenommen, der Typ Person hat die Untertypen (subtypes) Student und Angestellter. Dann ist jedes Objekt vom Typ Student oder Angestellter auch ein Objekt vom Typ Person. An eine Routine mit einem formalen Parameter vom Typ Person kann auch ein Argument vom Typ Student oder Angestellter übergeben werden. Man ist beim enthaltenden Polymorphismus auf dynamisches binden angewiesen. Definition: Ein Typ U ist ein Untertyp eines Typs T (bzw. T ist ein Obertyp von U) wenn eine Instanz von U überall verwendbar ist, wo eine Instanz von T erwartet wird. Dynamisches Binden Die auszuführende Methode wird erst während der Programmausführung festgestellt. Der Compiler kann das nicht vorher wissen. Seite 3 / 22

Statisches Binden Der Compiler kann die auszuführende Methode beim compilen festlegen. Überladen Eine Routine heißt ad-hoc-polymorph, wenn sie Argumente mehrerer unterschiedlicher Typen, die in keiner Relation zueinander stehen müssen, akzeptiert und sich f ur jeden dieser Typen anders verhalten kann. Eine überladene Methode kann Parameter unterschiedlicher Typen annehmen, und verhält sich je nach den übergebenen Typen anders. Überladen dient hauptsächlich zur syntaktischen Erleichterung, da Methoden mit ähnlicher Funktionalität so nicht anders benannt werden müssen. Typumwandlung Typumwandlung ist eine semantische Operation. Sie dient zur Umwandlung eines Wertes in ein Argument eines Typs, der von einer Routine erwartet wird. Vererbung Vererbung ermöglicht es, von einer Klasse eine andere abzuleiten. Man kann so Klassen erweitern oder Teile überschreiben. Sie erspart im Prinzip Schreibarbeit. Beim überschreiben gibt es meist Möglichkeiten auf überschriebene Variablen bzw Methoden zuzugreifen. Phasen des Softwareentwicklungsporzesses Analyse (die Aufgabe wird analysiert) Entwurf (es wird beschrieben wie das Programm die Aufgabe löst) Implementierung (Umsetzung des Entwurfs in das Programm) Verifikation (Überprüfung ob das Programm die Aufgabenstellung erfüllt) Wasserfallmodell Im traditionellen Wasserfallmodell werden diese Schritte einmal ausgeführt. Danach ist die Software fertig. Dieses Modell eignet sich gut für kleine Projekte oder Projekte mit klar definierten Anforderungen. Zyklische Softwareentwicklungsprozesse Hier werden diese Schritte solange wiederholt, bis die Software fertig ist. Beim ersten Durchlauf des Zyklus wird ein kleiner, wesentlicher teil der Aufgabenstellung gelöst. In jedem weiteren Durchlauf wird das Programm schrittweise erweitert, bis es fertig ist. Seite 4 / 22

Faktorisierung Das Zusammenfassen von zusammengehörigen Teilen des Programms(zB ersetzen von einer Codesequenz die öfters im Programm vorkommt durch eine Routine). Gute Faktorisierung erreicht man, wenn Änderungen am Programm an einer einzigen bzw wenigen Stellen auszuprogrammieren sind. Bei einer schlechten Faktorisierung müssten alle Stellen die geändert werden müssen, erst gefunden und dann geändert werden. In objektorientierten Sprachen dienen dazu Objekte. Man fasst die gesamte Funktionalität in diesen Einheiten zusammen. Allerdings muss man darauf achten, eine gute Zerlegung zu wählen. Man spricht von einer Refaktorisierung, wenn die Zerlegung in Objekte eines Programms nachträglich geändert werden muss. Refaktorisierungen sollte man möglichst früh durchführen, weil es zu dem Zeitpunkt noch am wenigsten Kosten verursacht. Klassen-Zusammenhalt Ein hoher Klassen-Zusammenhalt wird erzielt, wenn die Variablen und Methoden einer Klasse eng zusammenarbeiten und durch den Namen der Klasse gut beschrieben werden. Ein niedriger Klassen- Zusammenhalt besteht, wenn man die Klasse sinnändernd umbenennt. Der Klassen-Zusammenhalt sollte hoch sein. Das erreicht man durch gute Faktorisierung. Objekt-Kopplung Beschreibt die Abhängigkeit der Objekte voneinander. Diese sollte schwach sein. Das erreicht man durch gute Kapselung. Eine starke Objektkopplung besteht, wenn: die Anzahl der nach außen sichtbaren Methoden und Variablen hoch ist, im laufenden System Nachrichten (beziehungsweise Methodenaufrufe) und Variablenzugriffe zwischen unterschiedlichen Objekten häufig auftreten und die Anzahl der Parameter dieser Methoden hoch ist. Zwischen Klassen-Zusammenhalt und Objekt-Kopplung besteht ein enger Zusammenhang. Wenn der K-Z hoch ist, dann ist die O-K schwach. Das ist erstrebenswert. Seite 5 / 22

Wiederverwendung Programme Die meisten Programme werden im Hinblick darauf entwickelt, dass sie häufig (wieder)verwendet werden. Dadurch zahlt es sich erst aus, einen großen Aufwand zu betreiben, um die Programme handlich und effizient zu machen. Daten Auch Daten in Datenbanken und Dateien werden in vielen Fällen häufig wiederverwendet. Nicht selten haben Daten eine längere Lebensdauer als die Programme, die sie benötigen oder manipulieren. Erfahrungen Häufig unterschätzt wird die Wiederverwendung von Konzepten und Ideen in Form von Erfahrungen. Diese Erfahrungen werden oft zwischen sehr unterschiedlichen Projekten ausgetauscht. Code Viele Konzepte von Programmiersprachen, wie zum Beispiel enthaltender Polymorphismus, Vererbung und Generizität, wurden insbesondere im Hinblick auf die Wiederverwendung von Code entwickelt. Man kann mehrere Arten der Codewiederverwendung mit verschiedenen Wiederverwendungshäufigkeiten unterscheiden. Arten der Codewiederverwendung Codewiederverwendung erfordert beträchtliche Investitionen in die Wiederverwendbarkeit. Man soll diese tätigen, wenn ein tatsächlicher Bedarf dafür absehbar ist. Globale Bibliotheken Klassen die zu dieser Kategorie gehören werden sehr oft wiederverwendet, da sie ein stark grundlegendes Konzept haben. Nur einfache Klassen passen in diese Kategorie. Fachspezifische Bibliotheken Komplexere Klassen bzw Objekte lassen sich in fach -oder firmenspezifischen Bibliotheken unterbringen. Ein Beispiel dafür sind grafische Benutzeroberflächen, mit denen auch ein hoher Grad an Wiederverwendung erreicht wird. Projektinterne Wiederverwendung Hochspezialisierte Klassen die nur innerhalb eines Projekts wiederverwendet werden können, fallen in diese Kategorie. Man erreicht hier meist keine hohe Wiederverwendung, aber jede Wiederverwendung spart viel Zeit. Programminterne Wiederverwendung Ein und derselbe Programmcode kann in einem Programm sehr oft wiederholt ausgeführt werden, auch zu unterschiedlichen Zwecken. Durch die Verwendung eines Programmteils für mehrere Aufgaben wird das Programm einfacher, kleiner und leichter wartbar. Seite 6 / 22

Entwurfsmuster Entwurfsmuster dienen zum Austausch von kollektiver Erfahrung. Sie verallgemeinern Problemstellungen und bieten dafür Lösungen an. Teile eines Entwurfsmusters sind: Name Gibt der Problemstellung einen Namen, über den diese einfach identifizierbar ist. (zb Factory Method) Problemstellung Gibt Auskunft darüber, welches Problem durch dieses Entwurfsmuster gelöst werden soll. (zb bei der Factory Method: Eine Factory Method definiert eine Schnittstelle für die Objekterzeugung, wobei Unterklassen entscheiden, von welcher Klasse die erzeugten Objekte sein sollen; die tatsächliche Objekterzeugung wird in Unterklassen verschoben.) Lösung Beschreibt die Lösung des Problems und ist sehr allgemein gehalten. (zb bei der Factory Method: Factory Method enthält die Beschreibung Erklärungen dafür, wie die Klassenstrukturen aussehen, welche Abhängigkeiten zwischen den Klassen bestehen, und welche Arten von Methoden geeignet sind) Konsequenzen Ist im Prinzip eine Liste von Vor -und Nachteilen einer Lösung. (zb bei der Factory Method: höhere Flexibilität bei der Objekterzeugung, eine andere das Entstehen paralleler Klassenhierarchien mit einer oft großen Anzahl an Klassen.) Man muss bei der Verwendung von Entwurfsmustern darauf achten, dass das zu erstellende Programm deswegen nicht zu komplex wird. Man kann sie also zur Abwägung von bestimmten Möglichkeiten einer Designentscheidung in begrenztem Ausmaß heranziehen. Paradigmen Imperative Programmierung Die Programme bestehen aus Anweisungen, welche in fester Reihenfolge ausgeführt werden. Sprachelemente sind Variablen und Routinen. Für diese Art der Programmierung entwickelten sich viele Paradigmen. Die zwei wichtigsten sind die prozedurale bzw die objektorientierte Programmierung. Deklarative Programmierung Diese bewegt sich auf einem höheren Abstraktionsniveau als die imperative Programmierung. Es gibt keine zeitlich aufeinander folgenden Zustandsänderungen. Grundlegende Sprachelemente sind Symbole die man in mehrere Gruppen einteilen kann. Die zwei wichtigsten Paradigmen hier sind die funktionale Programmierung und die Logikorientierte Programmierung. Funktionale Programmierung Ausdrücke in dieser Art von Programmierung werden als Funktionen aufgefasst, die auf Funktionen angewendet werden. Diese Sprachen haben die Eigenschaft der referenziellen Transparenz. Das bedeutet, dass es egal ist wo der Ausdruck im Programm steht. Logikorientierte Programmierung Hier wird die Menge aller wahren Aussagen in einem Modell mittels Fakten beschrieben. Es wird eine Anfrage gestellt, und das Ergebnis ist ob und unter welchen Bedingungen die in der Anfrage enthaltene Aussage, wahr ist. Diese Sprachen spielen bei Datenbankabfragesprachen eine wichtige Rolle. Seite 7 / 22

Paradigmen für Modellierungseinheiten Programmierung mit Modulen Es wird großer Wert auf Modularisierungseinheiten gelegt. Dabei handelt es sich um Gruppierungen von Variablen, Routinen, Typen, Klassen usw. Ein Programm besteht aus mehreren Modulen. Module sind unverzichtbar, wenn man große Problemstellungen in kleinere aufspalten will. Programmierung mit abstrakten Datentypen Ein abstrakter Datentyp versteckt die interne Darstellung seiner Instanzen. Man greift durch die in der abstrakten Datenstruktur definierten Operationen auf diese zu. Einige Komponenten werden durch den Zugriff von außen geschützt. Die Programmierung mit abstrakten Datentypen entspricht der objektorientierten Programmierung, abgesehen davon, dass es keinen enthaltenden Polymorphisumus und keine Vererbung gibt. Generische Programmierung Generische Einheiten werden erst zur Laufzeit zu konkreten Datenstrukturen. Sie sind im Programm nur einmal definiert, können aber mehrere verschiedene Typen aufnehmen. Die generische Programmierung wird vor allem mit der objektorientierten Programmierung und der funktionalen Programmierung kombiniert. Seite 8 / 22

Kapitel 2 Enthaltender Polymorphismus und Vererbung Ersetzbarkeitsprinzip Ein Typ U ist ein Untertyp eines Typs T, wenn eine Instanz von U überall verwendbar ist, wo eine Instanz von T erwartet wird (zb bei Aufruf einer Routine mit Parameter bzw Zuweisung eines Objekts an eine Variable). Untertypbeziehungen Generell gilt, dass jeder Typ ein Untertyp von sich selbst ist. U ist ein Untertyp von T, wenn folgende Bedingungen erfüllt sind: 1) Für jede Konstante in T vom Typ A gibt es eine entsprechende Konstante in U vom Typ B, wobei B ein Untertyp von A ist (Nur lesend zugreifen: Man erwartet sich den Typ A beim Lesen der Konstante. Deshalb muß B ein Untertyp von A sein). 2) Für jede Variable in T gibt es eine entsprechende Variable in U, wobei die deklarierten Typen der Variablen gleich sind.(lesend und schreibend zugreifen: Obere nur Lesen- Bedingung muss erfüllt sein UND man soll eine Variable in T vom Typ A schreiben können, obwohl diese in U vom Typ B ist. Deshalb muss B ein Untertyp von A sein UND A ein Untertyp von B sein. Untertypbeziehungen sind antisymetrisch, deshalb muss A gleich B sein) 3) Für jede Methode in T gibt es eine entsprechende Methode in U, wobei der deklarierte Ergebnistyp der Methode in U ein Untertyp des Ergebnistyps der Methode in T ist, die Anzahl der formalen Parameter der beiden Methoden gleich ist und der deklarierte Typ jeden formalen Parameters in U ein Obertyp des deklarierten Typs des entsprechenden formalen Parameters in T ist (Für Ergebnistypen gilt: dasselbe wie für Konstanten(nur Lesezugriff). Für die formalen Parameter gilt: dasselbe wie für Variablen (zumindest in Java, in C++ nicht)). Der Compiler kann diese Eigenschaften überprüfen. Wenn man einen Untertyp bildet, können die deklarierten Typen der Elemente variieren. Je nachdem wie diese Typen variieren, spricht man von Kovarianz, Kontravarianz und Invarianz. Kovarianz Der deklarierte Typ eines Elements im Untertyp ist ein Untertyp des deklarierten Typs des entsprechenden Elements im Obertyp. Zum Beispiel sind deklarierte Typen von Konstanten und von Ergebnissen der Methoden (auch Ausgangsparametern) kovariant. Typen und die betrachteten darin enthaltenen Elementtypen variieren in dieselbe Richtung. Kontravarianz Der deklarierte Typ eines Elements im Untertyp ist ein Obertyp des deklarierten Typs des Elements im Obertyp. Zum Beispiel sind deklarierte Typen von formalen Eingangsparametern kontravariant. Typen und die betrachteten darin enthaltenen Elementtypen variieren in entgegengesetzte Richtungen. Invarianz Der deklarierte Typ eines Elements im Untertyp ist gleich dem deklarierten Typ des entsprechenden Elements im Obertyp. Zum Beispiel sind deklarierte Typen von Variablen und Durchgangsparametern invariant. Die betrachteten in den Typen enthaltenen Elementtypen variieren nicht. Seite 9 / 22

Vererbung in Java In Java werden Methoden nicht überschrieben sondern nur überladen. Es sind auch alle Typen invariant (außer Ergebnistypen ab Version 1.5). Kovariante Eingangsparametertypen und binäre Methoden widersprechen dem Ersetzbarkeitsprinzip. Es ist sinnlos, in solchen Fällen Ersetzbarkeit anzustreben. Binäre Methoden Der Begriff Binär bezieht sich darauf, dass der Name der Klasse in der Methode mindestens zwei mal vorkommt einmal als Typ von this und mindestens einmal als Typ eines expliziten Parameters. Binäre Methoden sind über den einfachen enthaltenden Polymorphismus, wie wir ihn hier verwenden, prinzipiell nicht realisierbar. Untertypen und Codewiederverwendung Das Ersetzbarkeitsprinzip eröffnet einige Möglichkeiten der Codewiederverwendung die nicht offensichtlich sind. zb eine Treiber-Software für eine Grafikkarte. Diese wurde bereits mehrfach in anderen Programmen verwendet. Wenn wir nun den Treiber verbessern, indem wir von dem alten Treiber erben und die Schnittstelle unverändert lassen. Natürlich können auch neue hinzugefügt werden. Programme die unseren alten Treiber verwenden können schnell auf den neuen umgerüstet werden und Programme die unseren alten Treiber verwenden funktionieren immer noch. Man soll auf Ersetzbarkeit achten, um Codewiederverwendung zu erreichen. Schnittstellen sollen stabil bleiben. Gute Faktorisierung hilft dabei. Die Stabilität von Schnittstellen an der Wurzel der Typhierarchie ist wichtiger als an den Blättern. Man soll nur Untertypen von stabilen Obertypen bilden. Man soll Parametertypen vorausschauend und möglichst allgemein wählen. Dynamisches Binden Der dynamische Typ ist zur Compilezeit nicht bekannt und kann sich zur Laufzeit vom deklarierten Typen unterscheiden. In Java wird, unabhängig vom statischen Typ, immer die Methode ausgeführt, die in der Klasse des Objekts definiert ist. Die Schnittstelle dieser Klasse entspricht dem spezifischsten dynamischen Typ der Variablen. Dynamisches binden kann auch sehr gut anstatt von switch-anweisungen verwendet werden. Nachträgliches Hinzufügen von Verzweigungsmöglichkeiten in switch-anweisungen kann in einem großen Programm sehr aufwendig sein. Wenn man eine Oberklasse hat, von der man alle Anredeart-Klassen ableitet, kann man neue Verzweigungen sehr schnell hinzufügen, und man muss bei den Methodenaufrufen nichts ändern. Seite 10 / 22

Client - Server - Beziehungen Zusicherungen Diese können vom Compiler (meistens) nicht überprüft werden. Man sollte sie trotzdem einhalten, da sie Bedeutend für die Ersetzbarkeit sind. Es gibt Programmiersprachen, wo man diese festlegen kann, ansonsten sollte man diese durch Kommentare beschreiben und händisch prüfen. Vorbedingungen Für deren Erfüllung ist der Client vor Ausführung der Methode verantwortlich (zb bei Parametern: Der übergebene Parameter muss ein Array aus ganzen zahlen und aufsteigend sortiert sein. Bei Zuständen: Es muss ein bestimmter Zustand des Servers erfüllt sein). Nachbedingungen Für deren Erfüllung ist der Server nach Ausführung der Methode verantwortlich (zb bei Methode: Nach dem Aufruf der Methode muss das erledigt sein, was bei deren Nachbedingung steht (zb ein Element muss in die Menge eingefügt worden sein)). Invarianten Für deren Erfüllung ist der Server vor und nach der Ausführung der Methode verantwortlich (zb Guthaben am Sparbuch muss immer positiv sein). Für direkte Zugriffe von Clients auf öffentlich zugängliche Variablen kann der Server diese Verantwortung aber nicht übernehmen. Zusicherungen gehören zum Typ eines Objekts dazu. Ein Objekt besteht demnach aus: dem Namen, der Schnittstelle und den Zusicherungen. Zusicherungen sollen stabil bleiben. Man sollte es vermeiden, zu detaillierte bzw zu undetaillierte Zusicherungen zu formulieren. Untertypen und Verhalten Zusicherungen die zu Typen gehören müssen bei der Verwendung von enthaltendem Polymorphismus berücksichtigt werden. Damit U ein Untertyp von T ist, müssen zusätzlich zu den vorher genannten Bedingungen folgende erfüllt sein: 1) Jede Vorbedingung auf einer Methode in T muss eine Vorbedingung der entsprechenden Methode in U implizieren. Vorbedingungen können also schwächer sein, dürfen aber nicht stärker werden (zb: Die alte Vorbedingung kann durch ein ODER schwächer werden). 2) Jede Nachbedingung auf einer Methode in U muss eine Nachbedingung auf der entsprechenden Methode in T implizieren. Nachbedingungen können also stärker werden, dürfen aber nicht schwächer werden (zb: Die alte Nachbedingung kann durch ein UND stärker werden). 3) Jede Invariante in U muss eine Invariante in T implizieren. Invarianten können also stärker werden, dürfen aber nicht schwächer werden (zb: Die alte Invariante kann durch ein UND stärker werden). Wenn Instanzvariablen durch andere Objekte verändert werden, müssen die Invarianten aber in U und T übereinstimmen. In manchen Programmiersprachen(wo Zusicherungen formuliert werden können) können Zusicherungen auch automatisch überprüft werden. In Java ist das nicht der Fall. Man muss also sicherstellen, dass: Untertypbeziehungen eingehalten werden, Clients die Vorbedingungen erfüllen und Server die Nachbedingungen und Invarianten erfüllen. Seite 11 / 22

Abstrakte Klassen Im Zusammenhang mit enthaltendem Polymorphismus muss man eine Klasse oft nicht detailliert beschreiben, es reicht die Festlegung des Typs. Abstrakte Klassen kann man nicht instanzieren, nur deren Unterklassen die alles implementieren, was in der abstrakten Klasse definiert wurde (in Java gibt es auch noch Interfaces). Da abstrakte Klassen keine Implementierungen enthalten, sind sie meist stabiler als gewöhnliche Klassen. Deshalb sollten Obertypen und Parametertypen wenn möglich abstrakt sein. Vererbung versus Ersetzbarkeit Vererbung kann zur direkten Codewiederverwendung eingesetzt werden und ist deshalb auch unabhängig vom Ersetzbarkeitsprinzip sinnvoll. Arten von Beziehungen zwischen Klassen Untertypbeziehung Ist die Beziehung die auf dem Ersetzbarkeitsprinzip beruht (wie bis jetzt beschrieben). Vererbungsbeziehung Ist eine Beziehung zwischen zwei Klassen, bei denen die eine Klasse durch Abänderung der anderen Klasse entsteht. Es ist wünschenswert, dass dabei Code aus der Oberklasse wiederverwendet wird. Für diese Beziehung ist das Ersetzbarkeitsprinzip irrelevant. Reale-Welt-Beziehung Das sind Beziehungen die sich bereits in der Analyse-Phase herauskristallisieren (zb Person ist ein Student). Diese Beziehungen entwickeln sich normalerweise zu Untertypbeziehungen bzw zu Vererbungsbeziehungen. Falls das nicht der Fall ist, muss man Refaktorisieren. Bei der reinen Vererbungsbeziehung ist die Codewiederverwendung ausschlaggebend. Hier kann Bag von LargSet viel Code erben. Bei der Untertypbeziehung lässt man sich von Konzepten beziehungsweise Typen leiten. Man will hier im Programm selten zwischen LargeSet und SmallSet unterscheiden. Bag und LargeSet stehen nicht in Beziehung zueinander, weil die Methoden zum Hinzufügen eines Elementes sich unterscheiden. Seite 12 / 22

Vererbung und Codewiederverwendung Codewiederverwendung kann unter Umständen viel bringen, aber man darf deshalb nicht die Untertypbeziehungen weglassen. Man erreicht eine bessere Codewiederverwendung, aber man kann keinen Untertyp mehr dort verwenden, wo dessen Obertyp verwendbar wäre. Dadurch wirken sich dann kleine Änderungen überall im Programm aus. Wiederverwendung durch das Ersetzbarkeitsprinzip ist wesentlich wichtiger als direkte Wiederverwendung durch Vererbung. Man sollte deshalb aber nicht gleich auf Vererbungsbeziehungen verzichten. Oft kann man Untertypbeziehungen mit Vererbungsbeziehungen kombinieren. Java Details static Variablen, die mit static deklariert werden, gehören nicht zu den verschiedenen Instanzen der Klasse, sondern zu der Klasse selbst. Man kann auf diese also zugreifen ohne Instanzen zu erstellen bzw anzugeben. Methoden, die mit static deklariert werden, gehören ebenfalls zur Klasse. In ihnen kann nicht auf Instanzvariablen zugegriffen werden. Das Schlüsselwort this funktioniert deshalb auch nicht. static final Konstanten sind ein Spezialfall von Klassenvariablen. Deren Wert kann nach der Initialisierung nicht mehr geändert werden. static initializers Sind im Prinzip Konstruktoren für Klassenvariablen. Sie dienen also deren Initialisierung. Das Schlüsselwort lautet static { } Seite 13 / 22

Geschachtelte Klassen Statische geschachtelte Klassen Wie statische Methoden dürfen statische geschachtelte Klassen nur auf Klassenvariablen der umschließenden Klasse zugreifen und statische Methoden der umschließenden Klasse aufrufen. Instanzvariablen und Instanzmethoden der umschließenden Klasse sind nicht zugreifbar. Instanzen der Klasse EnclosingClass.StaticNestedClass im Beispiel können durch new EnclosingClass.StaticNestedClass() erzeugt werden. Innere Klassen Instanzvariablen und Instanzmethoden aus EnclosingClass können in InnerClass uneingeschränkt verwendet werden. Innere Klassen dürfen jedoch keine statischen Methoden und keine statischen geschachtelten Klassen enthalten. Beispielsweise wird eine Instanz von EnclosingClass.InnerClass durch a.new InnerClass() erzeugt, wobei a eine Variable vom Typ EnclosingClass ist. Abgesehen davon entsprechen geschachtelte Klassen den nicht geschachtelten Klassen. Sie können abstrakt sein und von anderen Klassen erben. Konstruktoren Beim Erzeugen einer neuen Instanz einer Unterklasse wird nicht nur der Konstruktor der Unterklasse, sondern auch der Konstruktor der Oberklasse aufgerufen. Wenn man sich nicht explizit darum kümmert, wird der Konstruktor der Oberklasse automatisch ohne Argumente aufgerufen. Variablen Überschreiben Eine Variable der Oberklasse kann mit super angesprochen werden. Wenn man Variablen ansprechen will, die weiter oben in der Klassenhierarchie stehen, muss man einen Typecast machen. Methoden Überschreiben Methoden werden in Java nur überschrieben, wenn der Name, die Parameterzahl und die Parametertypen der Methode gleich sind. Methoden der Oberklasse kann man mit super aufrufen. Methoden die weiter oben in der Klassenhierarchie stehen müsste man durch Typecasts aufrufen, was aber durch dynamisches binden verhindert wird. Mit final deklarierte Methoden dürfen nicht überschrieben werden. Diese sollten aber höchstens aus Sicherheitsgründen verwendet werden, weil sie die Wartbarkeit einschränken. Seite 14 / 22

Zugriffskontrolle in Java Pakete In einem Paket sollen alle Klassen stehen, die eng zusammenarbeiten. Der Name des Paketes ist der Name des Ordners, in dem sich die Klassen befinden. Man ruft Methoden von Paketen auf, indem man den Pfad getrennt mit Punkten statt Slashes dem Methodenaufruf voranstellt. Um sich Schreibaufwand zu ersparen, gibt es das import-schlüsselwort. Das package-schlüsselwort dient dazu, um sicherzustellen, dass einzelne Klassen die zu einem Paket gehören, nicht aus dem Kontext gerissen werden. Interfaces in Java Es ist oft hinderlich, dass in Java nur Einfachvererbungen unterstützt werden. Aus diesem Grund gibt es Interfaces. Interfaces sind eingeschränkte abstrakte Klassen in denen alle Methoden abstrakt sind. Sie unterstützen Mehrfachvererbung. Alle Methoden eines Interfaces sind public. Seite 15 / 22

Kapitel 3 Generizität und Ad-hoc-Polymorphismus Generizität Generische Klassen, Typen und Routinen enthalten Typparameter, für die Typen eingesetzt werden können. Sie unterstützt die Wiederverwendbarkeit und ist im Wesentlichen ein statischer Mechanismus. In Java gibt es Generizität erst ab Version 1,5. Anstatt von expliziten Typen gibt man Typparameter an. Das sind Namen, die später durch Typen ersetzt werden. Ein gutes Beispiel für eine sinnvolle Verwendung von Generizität sind Listen. Diese sollten alle möglichen Typen enthalten können, ohne den Programmcode neu schreiben zu müssen. Generizität erspart also nicht nur Schreibarbeit, sondern es macht das übersetzte Programm kürzer und effizienter. Für den Compiler bzw zur Laufzeit bedeutet Generizität kaum einen Mehraufwand. Einfache Generizität in Java Generische Klassen und Interfaces haben einen oder mehrere Typparameter, die in spitze Klammern geschrieben und durch Beistriche getrennt sind. Innerhalb der Klassen und Interfaces lassen sich die Typparameter fast wie Klassen verwenden. Generizität bietet statische Typsicherheit, weil zb in eine Instanz von List<String> nur Strings eingefügt werden können. Der Compiler meldet einen Fehler falls man sich nicht daran hält. Generische Methoden Auch Methoden können generisch sein. Man muss dafür in Spitzen Klammern vor dem Ergebnistypen den Typparameter angeben (zb public <A> A hallo( ) { ). Diese haben den Vorteil, dass man die für die Typparameter verwendeten Typen nicht explizit angeben muss. Gebundene Generizität in Java Die einfache Generizität ist zwar elegant und sicher, reicht aber nicht um alle Anwendungsgebiete abzudecken. Bei einer einfachen generischen Klasse oder Methode ist über den Typparameter nichts bekannt. Man weiß nicht ob er bestimmte Methoden oder Variablen hat. Dafür gibt es gebundene Typparameter. In Java wird das durch eine so genannte Schranke erreicht. Diese sagt aus, dass nur Untertypen dieser Schranke als Typparameter verwendet werden dürfen (zb class Hashtable<Key extends Hashable, Value>). Diese Informationen liegen wieder statisch vor. Nach so einem extends kann eine Klasse bzw können mehrere Interfaces stehen. Wenn kein extends angegeben wird, wird automatisch extends Object verwendet. Typparameter dürfen auch rekursiv verwendet werden (zb <A extends Comparable<A>>). Falls rekursive Typparameter verwendet werden spricht man von F-Gebundener Generizität. Bei dieser ist zu beachten, dass sie keine impliziten Untertypbeziehungen unterstützt (zb wenn Y von X abgeleitet ist, besteht zwischen List<X> und List<Y> keine Untertypbeziehung; Untertypbeziehungen bestehen nur dort, wo sie explizit durch extends verinbart werden). Das Nichtunterstützen impliziter Untertypbeziehungen aus Sicherheitsgründen hat auch einen Nachteil (zb void drawall (List<Polygon> p) lässt nur Typparameter vom Typ Polygon zu, keine Untertypen davon). Dieser wird aber durch gebundene Wildcards wieder aufgehoben (zb void drawall (List<? extends Polygon> p) nimmt alle Typparameter, die Untertypen von Polygon sind, sofern p nur gelesen wird (Kovariant)). Folgender Kontext ist ebenfalls zulässig (zb void addsquares (List<? super Square> to ) nimmt alle Typparameter, die Obertypen von Square sind, sofern to nur geschrieben wird (Kontravariant)). In Java kann man mit Typparametern keine neuen Objekte erzeugen. Seite 16 / 22

Verwendung von Generizität Generizität ist überall dort sinnvoll, wo es die Wartbarkeit erhöht. Gleich strukturierte Klassen oder Routinen Falls es solche gibt, sollte man Generizität so früh wie möglich verwenden, weil es den Wartungsaufwand reduziert. Beispiele für solche Klassen sind Containerklassen wie zb Listen und Stacks. Abfangen erwarteter Änderungen Wenn man weiß, dass sich die Typen der formalen Parameter wahrscheinlich ändern werden, sollte man diese generisch wählen, damit man später nicht die Schnittstellen ändern muß. Das wäre mit großem Aufwand verbunden. Ein Anwendungsbeispiel ist eine Bank, die anfangs nur Euro-Konten verwalten will. Wenn sich später herausstellt, dass sie auch gerne Dollar nehmen würden, geht die Umstellung sehr rasch, wenn man die Typen der formalen Parameter generisch wählt. Verwendbarkeit Generizität und Untertypbeziehungen sind oft gegeneinander austauschbar, obwohl sie sich sinnvoll ergänzen. Man kann aber auf keines der beiden Konzepte verzichten, weil zb Generizität in Listenklassen statische Typsicherheit bietet, was man mit Untertypbeziehungen nicht erreichen kann. Andererseits kann man zb ohne Untertypbeziehungen keine Listen erstellen, die mehrere Typen enthalten können. Laufzeiteffizienz Man verliert durch Generizität kaum an Laufzeit, weshalb man Effizienzüberlegungen zum Einsatz der Generizität beiseite lassen sollte. Natürlichkeit Als Anfänger sollte man sich nicht auf die intuitive Lösung verlassen, sondern genau überlegen was man erreichen will. Erfahrene Programmierer wählen automatisch die bessere Lösung. Arten der Generizität Homogene Übersetzung Diese wird in Java verwendet. Jede generische bzw nicht generische Klasse wird in genau eine Klasse mit JVM-Code übersetzt. Gebundene Typparameter werden durch ihre erste Schranke ersetzt. Ungebundene Typparameter werden durch Object ersetzt. Rückgabewerte von Methoden einer Instanz eines Typparameters werden dynamisch umgewandelt. Heterogene Übersdetzung Diese wird in templates in C++ verwendet. Es wird für jede Verwendung einer generischen Klasse mit unterschiedlichen Typparametern eigener Code erzeugt. Man hat zwar erhöhten Speicherplatzbedarf, aber die Effizienz erhöht sich. In manchen Programmiersprachen ist nicht im vornhinein festgelegt, welche Art der Übersetzung verwendet wird. Seite 17 / 22

Typabfragen und Typumwandlung Verwendung dynamischer Typinformation Mit der Methode getclass() die Object besitzt kann die Klasse eines Objekts direkt abgefragt werden. Davon macht man allerdings nur selten Gebrauch. Meistens wird der instanceof-operator verwendet. Dieser liefert true, wenn in var instanceof Class die Variable Var ein Untertyp der Klasse Class ist. Typumwandlungen sind auf Referenzobjekten nur durchführbar, wenn der Ausdruck, dessen deklarierter Typ in einen anderen Typ umgewandelt werden soll, tatsächlich den gewünschten Typ oder einen Untertyp davon als dynamischen Typ hat oder gleich null ist. Dies wird zur Laufzeit überprüft und löst bei Verletzung der Regel eine Ausnahmebehandlung aus. Man soll Typumwandlungen allerdings sparsam einsetzen, weil Fehler zur Laufzeit nicht immer erkannt werden. Man sollte dynamische Typabfragen durch dynamisches Binden ersetzen. Das kann in folgenden Fällen Probleme bereiten (gilt für x.dosomething() ): 1) Der deklarierte Typ von x ist zu allgemein; die einzelnen Alternativen decken nicht alle Möglichkeiten ab. Die statische Typsicherheit von Java wird umgangen. In dieser Situation ist eine Refaktorisierung des Programms angebracht. 2) Die Klassen, die dem deklarierten Typ von x und dessen Untertypen entsprechen, können nicht erweitert werden. Als (recht aufwändige) Lösung kann man parallel zur unveränderbaren Klassenhierarchie eine gleich strukturierte Hierarchie aufbauen, deren Klassen (Wrapper- Klassen) die zusätzlichen Methoden beschreiben. 3) Manchmal ist die Verwendung dynamischen Bindens schwierig, weil die einzelnen Alternativen auf private Variablen und Methoden zugreifen. Methoden anderer Klassen haben diese Information nicht. Oft lässt sich die fehlende Information durch Übergabe geeigneter Argumente beim Aufruf der Methode verfügbar machen. 4) Der deklarierte Typ von x kann sehr viele Untertypen haben. Wenn dosomething nicht in einer gemeinsamen Oberklasse in der Bedeutung von dosomethingofanytype implementierbar ist, beispielsweise weil der deklarierte Typ von x ein Interface ist, muss dosomething in vielen Klassen auf gleiche Weise implementiert werden. Das bedeutet einen Mehraufwand für die Wartung. Der Grund dafür liegt in der fehlenden Unterstützung der Mehrfachvererbung in Java. Durch Refaktorisierung und Verwendung geeigneter Entwurfsmuster lassen sich diese Probleme abschwächen oder vermeiden. Typumwandlungen zwischen primitiven Datentypen sind sicher. Typumwandlungen zwischen primitiven Typen und Referenztypen werden in Java nicht unterstützt. Seite 18 / 22

Typumwandlungen und Generizität Der Vorteil von Generizität liegt in erster Linie in der höheren Typsicherheit. Wenn man Typumwandlungen verwendet, soll man darauf achten, dass diese sicher sind: 1) Umwandlungen des deklarierten Typs in einen Obertypen davon (up-cast) 2) Dynamische Typabfrage vor dem Umwandeln, die sicherstellt, dass das Objekt einen entsprechenden dynamischen Typ hat (down-cast). Man muss darauf achten, dass in den Zusicherungen Annahmen beim Scheitern des Typvergleichs festgelegt sind. 3) Programmierung so, als ob man Generizität verwendet; Programm händisch auf Typfehler überprüfen; homogene Übersetzung durchführen (down-cast). Wenn die Programmiersprache Generizität unterstützt, sollte man diese Möglichkeit nicht verwenden. Wenn List<A> und Iterator<A> zwei generische Klassen sind, dann sind List und Iterator so genannte raw types. Bei den Ausdrücken mit der spitzen Klammer erfolgt eine statische Typüberprüfung durch den Compiler, bei den raw types nicht. Kovariante Probleme Typen von Eingangsparametern können nur kontravariant sein, weil kovariante Eingangsparameter das Ersetzbarkeitsprinzip verletzen. Man nennt Aufgabenstellungen, bei denen man kovariante Eingangsparameter braucht, Kovariante Probleme. Zur Lösung dieser Probleme kann man dynamische Typabfragen und Typumwandlungen verwenden. zb: Man hat eine Klasse Tier, die von Rind und Tiger extended wird und eine Klasse Futter, die von Gras und Fleisch extended wird. In der Klasse Tier gibt es die Methode friss(futter). Wenn man diese jetzt in Rind oder Kuh überschreibt, muss man überprüfen, ob man Gras oder Fleisch bekommen hat, bevor man es ißt (ein Tiger wird kein Gras essen). In diesem Beispiel könnte man die allgemeine Methode friss der Klasse Tier entfernen, und nur den beiden spezialisierten Klassen jeweils eine Methode mit dem speziellen Futter anbieten. Dadurch erreicht man eine Vermeidung des kovarianten Problems. Überladen versus Multimethoden In Java und vielen anderen Programmiersprachen erfolgt das dynamische Binden einer Methode über den dynamischen Typ der aufrufenden Instanz. Der dynamische Typ des Parameters entscheidet dabei nicht darüber welche Methode aufgerufen wird, sondern nur der statische Typ des Parameters. Wenn der dynamische Typ des Parameters die Methodenwahl beeinflusst, handelt es sich nicht um eine überladene Methode, sondern um eine Multimethode. Unterschiede zwischen Überladen und Multimethoden Wenn man Multimethoden verwendet, braucht man die Typen der Parameter nicht mit instenceof zu überprüfen, da die Methode nur dann aufgerufen wird, wenn der Parameter den dynamischen Typ hat, der im Methodenkopf drinnen steht. Beim verwenden von Multimethoden wird normalerweise immer die speziellste Methode aufgerufen, aber es kann auch passieren, dass diese nicht existiert. Compiler können dies aber überprüfen. Oder es wird immer die erste Methode die zum Aufruf passt, aufgerufen. Seite 19 / 22

Simulation von Multimethoden DA BIN ICH AUSGESTIEGEN Ausnahmebehandlung in Java Ausnahmen sind in Java Objekte, die Instanzen von Throwable sind. Verwendet werden meist dessen Unterklassen Error und Exception. Unterklassen von Error werden für schwerwiegende Fehler im Java-Laufzeitsystem verwendet. Diese Ausnahmen führen meist zur Beendung des Programms. Unterklassen von Exception werden in zwei Bereiche gegliedert. Der eine sind selbst definierte Ausnahmen, der andere sind Instanzen von RuntimeException (zb IndesOutOfBoundsException oder NullPointerException). Ausnahmen werden mit throw und einem anschließenden Klassennamen geworfen. Wenn eine Ausnahme ausgelöst wird, wird im Programm nach einer geeigneten stelle gesucht, um das Programm fortzuführen (ausgelöst im try block, springt in den catch block). Falls in einem try bzw catch Block noch eine Ausnahme ausgelöst wird, sucht das Programm nach dem try Block der eins höher ist. Wenn keiner gefunden wird, gibt die Methode die Exception zurück und es wird im Aufrufer nach einem catch gesucht. Das geht so weiter, bis die Ausnahme abgefangen wird oder das Programm wird beendet. Das Ersetzbarkeitsprinzip verlangt, dass die Ausführung einer Methode eines Untertyps nur solche Ausnahmen zurück liefern kann, die bei Ausführung der entsprechenden Methode des Obertyps erwartet werden. Daher dürfen Methoden in einer Unterklasse in der throws-klausel nur Typen anführen, die auch in der entsprechenden throws-klausel in der Oberklasse stehen. Ein finally-block kann nach einem try und beliebig vielen catch-blöcken stehen. Der finally-block wird auf jeden Fall ausgeführt. Er eignet sich besonders zum Freigeben von Ressourcen die Aufgrund von Ausnahmen nicht mehr benötigt werden. Einsatz von Ausnahmebehandlungen Bei den Punkten 1 und 2 handelt es sich um fehlerhafte Programmzustände, die durch Ausnahmen eingegrenzt werden sollen. Bei den Punkten 3 und 4 handelt es sich um Situationen, bei denen Ausnahmen von Programmierern gezielt verwendet werden, um den Programmfluss abzukürzen oder Einschränkungen des Typsystems zu umgehen. Unvorhergesehene Programmabbrüche Wird eine Ausnahme nicht abgefangen, kommt es zu einem Programmabbruch. Die entsprechende Bildschirmausgabe enthält genaue Informationen über Art und Ort des Auftretens der Ausnahme. Kontrolliertes Wiederaufsetzen Nach aufgetretenen Fehlern soll das Programm fortgesetzt werden können. Das ist vor allem im praktischen Einsatz des Programms wichtig. Es bietet vielleicht nur mehr eingeschränkte Dienste an, stürzt aber nicht ganz ab. Ausstieg aus Sprachkonstrukten Ausnahmen dieser Art erlauben das vorzeitige Abbrechen der Ausführung von Blöcken, Kontrollstrukturen und Methoden in außergewöhnlichen Situationen. Das Auftreten solcher Ausnahmen wird von Programmierern erwartet. Es ist daher relativ leicht, entsprechende Ausnahmebehandlungen durchzuführen, die eine sinnvolle Weiterführung des Programms erlauben. Rückgabe alternativer Ergebniswerte In Java und vielen anderen Sprachen kann eine Methode nur Ergebnisse eines bestimmten Typs liefern. Wenn in der Methode eine unbehandelte Ausnahme auftritt, wird an den Aufrufer statt eines Ergebnisses die Ausnahme zurückgegeben, die er abfangen kann. Damit ist es möglich, dass die Methode an den Aufrufer in Ausnahmesituationen Objekte zurückgibt, die nicht den deklarierten Ergebnistyp der Methode haben. Seite 20 / 22

Hinweise zur Verwendung von Ausnahmen Aus Gründen der Wartbarkeit soll man Ausnahmen und Ausnahmebehandlungen nur in echten Ausnahmesituationen und sparsam einsetzen, da ansonsten das Programm unleserlich wird und man oft über mehrere Ebenen die auftretenden Ausnahmen berücksichtigen muss. Man soll Ausnahmen nur einsetzen, wenn dadurch die Programmlogik vereinfacht wird. Das kann zb durch Ersetzen von vielen bedingten Anweisungen durch eine catch-klausel erreicht werden. Immer dann, wenn ein bestimmter Ergebniswert fehlerhafte Programmzustände anzeigt, ist es ratsam, statt diesem Wert eine Ausnahme zu verwenden. Diese Verwendung von Ausnahmen ist zwar nicht lokal, aber die Verwendung der speziellen Ergebniswerte erzeugt ebenso nicht-lokale Abhängigkeiten im Programm. Seite 21 / 22

Kapitel 4 Softwareentwurfsmuster Dieses Kapitel fehlt noch, kommt aber zur Prüfung. Im Forum gibt s auch dazu eine andere Zusammenfassung! Seite 22 / 22