Vererbung und Polymorphismus Programmiermethodik Eva Zangerle Universität Innsbruck Überblick Vererbung allgemein Einführung Java – Ein erster Überblick Objektorientierung Vererbung der Spezifikation in Java Vererbung der Implementierung in Java Vererbung und Polymorphismus Ausnahmebehandlung Pakete und Javadoc Spezielle Themen Abstrakte Klassen Die Klasse Object Generische Programmierung Java Collection-Framework Libraries Streams Unit-Tests GUI-Programmierung Entwurfsmuster Java Virtual Machine Ausblick Programmiermethodik - Objektorientierung 2 Vererbung allgemein Vererbung allgemein • Klassen modellieren Dinge der realen Welt. • Klassen sind oft Gruppierungen von gleichartigen Objekten. • Diese Dinge kommen oft in verschiedenen Varianten vor, die man durch Klassifikation hierarchisch gliedern kann. • Programme sollten mit den verschiedenen Varianten arbeiten und in bestimmten Situationen Varianten nicht unbedingt unterscheiden (gleich behandeln). • Java bietet die Möglichkeit hierarchische Klassenstrukturen zu bilden und Varianten von Objekten gleich zu behandeln. Programmiermethodik - Vererbung und Polymorphismus 4 Beispiel für Vererbungshierarchie Typebene Person Student Employee Professor Personen die weder Studenten noch Bedienstete sind Assistant InstanzEbene Person Employee Student Programmiermethodik - Vererbung und Polymorphismus Professor Assistant 5 Unterklassen und Oberklassen • Eine Klasse S ist eine Unterklasse der Klasse A, wenn S die Spezifikation von A erfüllt, umgekehrt aber A nicht die Spezifikation von S (A ist eine Oberklasse von S). Beziehung zwischen A und S wird Spezialisierung genannt. Beziehung zwischen S und A wird Generalisierung genannt. Programmiermethodik - Vererbung und Polymorphismus [Lahres 2006] 6 Prinzip der Ersetzbarkeit • Wenn eine Klasse B eine Unterklasse der Klasse A ist, dann können in einem Programm alle Exemplare der Klasse A durch Exemplare der Klasse B ersetzt werden und es gelten weiterhin alle zugesicherten Eigenschaften der Klasse A. Exemplare der Unterklasse sind gleichzeitig Exemplare der Oberklasse in Bezug auf die der Oberklasse zugrunde liegende Spezifikation. • Vorbedingungen, Nachbedingungen und Invarianten gelten auch dann, wenn Exemplare der Oberklasse durch Exemplare der Unterklasse ersetzt werden. • Beispiele folgen noch! Programmiermethodik - Vererbung und Polymorphismus 7 Kategorien von Klassen • Klassen können kategorisiert werden. • Abhängig davon, in welchem Umfang sie selbst für die von ihnen spezifizierte Schnittstelle auch Methoden anbieten. Kategorien: 1. 2. 3. Konkrete Klassen Rein spezifizierende Klassen (Schnittstellen-Klassen) Abstrakte Klassen [Lahres 2006] Programmiermethodik - Vererbung und Polymorphismus 8 Konkrete Klassen • Stellen für alle von der Klasse spezifizierte Operationen auch Methoden bereit. • Es können Instanzen erzeugt werden. • Wurden bisher in dieser Vorlesung besprochen. • Stellt im Grunde auch Schnittstellen zur Verfügung, diese sind jedoch auch implementiert. public class ArrayStack { private final String[] data; private int position = 0; public void push(String s) { if (this.position < this.data.length) { this.data[this.position] = s; this.position++; } else { System.err.println("could not insert"); } } Programmiermethodik - Vererbung und Polymorphismus 9 Schnittstellen-Klassen • Schnittstellen-Klassen (Interfaces) Dienen alleine der Spezifikation einer Menge von Operationen ("was"). Für keine Operation wird eine Implementierung bereitgestellt ("wie"). Es können keine Instanzen erzeugt werden. Trennung Spezifikation vs. Implementierung • „Eine Schnittstelle implementieren“ Unterklasse implementiert die Schnittstelle, wenn eine Unterklasse einer Schnittstellen-Klasse Methoden für alle von der Schnittstelle spezifizierten Operationen bereitstellt. • Einsatz Bei statisch typisierten Programmiersprachen (wie Java) Gemeinsamer Typ für Klassen, die Interfaces implementieren. public interface Stack { String pop(); // get top element int size(); // get no of elements void push(String s); // push element on top } Programmiermethodik - Vererbung und Polymorphismus 10 Abstrakte Klassen • Abstrakte Klassen Stellen meist für mindestens eine der spezifizierten Operationen keine Implementierung bereit. Spezialfall: Alle Methoden implementiert, Klasse aber als abstrakt markiert (siehe Abschnitt über abstrakte Klassen) Es kann keine direkten Instanzen geben. Alle Instanzen einer abstrakten Klasse müssen gleichzeitig Instanzen einer nicht abstrakten Unterklasse sein. Zwischenstufe zwischen Interfaces und konkreten Klassen. • Abstrakte Methoden Erlauben eine Operation für eine Klasse zu definieren, ohne dafür eine Methodenimplementierung zur Verfügung zu stellen. Methode dient nur zur Spezifikation. Eine Implementierung erfolgt in einer abgeleiteten konkreten Unterklasse. Programmiermethodik - Vererbung und Polymorphismus 11 Abstrakte Klassen public abstract class DefaultStack implements Stack { // provide default implementation for peek public Object peek() { Object o = this.pop(); this.push(o); return o; } // leave remaining stack operations open // for concrete classes public abstract Object pop(); public abstract boolean isEmpty(); public abstract void push(Object obj); } Programmiermethodik - Vererbung und Polymorphismus 12 Polymorphismus • Polymorph = „vielgestaltig“ • Eine Variable oder eine Methode kann gleichzeitig mehrere Typen haben. • Objektorientierte Sprachen sind polymorph. (konventionelle Sprachen wie zum Beispiel Pascal sind monomorph) • Verschiedene Arten (siehe rechts) Programmiermethodik - Vererbung und Polymorphismus Polymorphismus Universeller Polymorphismus Ad-hoc Polymorphismus Parametrischer Polymorphismus Überladen Inklusions-/ Vererbungspolymorphismus Typumwandlung 13 Polymorphismus (Arten) • Universeller Polymorphismus Ein Name oder Wert kann theoretisch unendlich viele Typen besitzen. Die Implementierung einer universell polymorphen Operation führt generell gleichen Code unabhängig von den Typen Ihrer Argumente aus. • Ad-hoc-Polymorphismus Ein Name oder ein Wert kann nur endlich viele verschiedene Typen besitzen. Typen sind zur Übersetzungszeit bekannt. Ad-hoc-polymorphe (also überladene) Operationen können abhängig von den Typen ihrer Argumente unterschiedlich implementiert sein. Programmiermethodik - Vererbung und Polymorphismus 14 Polymorphismus in dieser Vorlesung • Ad-hoc-Polymorphismus wurde schon behandelt Siehe Typumwandlung Siehe Überladen von Methoden • Universeller Polymorphismus Parametrischer Polymorphismus wird noch behandelt (Generische Programmierung). Inklusionspolymorphismus ist zentrales Thema dieses Foliensatzes. • Inklusionspolymorphismus Ersetzbarkeitsprinzip Dynamische Polymorphie Programmiermethodik - Vererbung und Polymorphismus 15 Vererbung der Spezifikation in Java Interfaces • Inhalt Methoden-Spezifikation ohne Rumpf Sind abstrakte Methoden Sind immer public Keine Konstruktoren Konstanten Alle Objektvariablen sind immer public static final (nur Konstanten). • Implementierung Eine Klasse kann Interfaces implementieren. Programmiermethodik - Vererbung und Polymorphismus 17 Exkurs: final • verschiedene Dinge können als final gekennzeichnet werden • Klassenvariablen • Objektvariablen • lokale Variablen • Methodenparameter • Methoden und Klassen (später) • Bedeutung • finale Variable oder Parameter kann nur maximal einmal einen Wert zugewiesen bekommen, danach besteht nur noch Lesezugriff • bei Klassenvariablen, Objektvariablen und Parametern geschieht dies genau einmal • Klassenvariablen: bei Deklaration oder im statischen Initialisierungsblock • Objektvariablen: bei Deklaration oder im Konstruktor • Parameter: beim Aufruf der Methode • Achtung bei Variablen von Referenztypen: Referenz bleibt unverändert, aber Objekt kann sich ändern Programmiermethodik - Vererbung und Polymorphismus 18 Klassen und Interfaces • Klassen implementieren Interfaces (mit implements). • Man kann keine Objekte von Interfaces anlegen. • Eine Klasse kann mehrere Interfaces implementieren. • Ein Interface kann von mehreren Klassen implementiert werden. • Die Implementierung eines Interfaces muss die Spezifikation erfüllen. Alle Methoden müssen implementiert werden (Ausnahmen werden noch besprochen; Eclipse-Hilfestellung). • Die entsprechenden Konstruktoren müssen in den Klassen implementiert werden (nicht im Interface). Programmiermethodik - Vererbung und Polymorphismus 19 Beispiel (Komplexe Zahlen) • Einfaches Interface für komplexe Zahlen public interface Complex { double getReal(); double getImag(); double getDistance(); double getPhase(); } • Alle Methoden sind implizit public und abstract. Könnte man angeben, muss man aber nicht. • Jede Klasse, die dieses Interface implementiert, muss die 4 angegebenen Methoden implementieren. • Beispiel: Siehe externes Beispiel Complex.java, Polar.java und Cartesian.java Programmiermethodik - Vererbung und Polymorphismus 20 Externes Beispiel in Worten • Klasse Cartesian und Polar implementieren das Interface Complex. • Beide Klassen implementieren die vorgegebenen Methoden (jeweils unterschiedlich). • Beide Klassen geben zusätzlich einen Konstruktor an. Die Form des Konstruktors wird nicht vom Interface vorgegeben. Es könnten noch mehrere Konstruktoren angegeben werden. Es können zusätzliche Methoden angegeben werden (add, sub etc.) – d.h. es gibt keine Einschränkung für weitere Methoden. • Beide Klassen sind gleichberechtigte und unabhängige Implementierungen von Complex. Programmiermethodik - Vererbung und Polymorphismus 21 Polymorphismus • Interfaces definieren einen Typ. Können in Variablendeklarationen, Parameterlisten, und als Ergebnistyp von Methoden verwendet werden. c ist vom Typ Complex. • Warum funktioniert das Beispiel? Alle Klassen implementieren das Interface Complex. Die Objekte dieser Klassen können einer Variable vom Typ Complex zugewiesen werden. Die Variable c kann daher auf unterschiedliche Objekte zeigen – Inklusionspolymorphismus. Programmiermethodik - Vererbung und Polymorphismus 22 Inklusionspolymorphismus • Einer Variable können Objekte unterschiedlichen Typs zugeordnet werden. Typ der Variable beschreibt nur Schnittstelle. Objekte, deren Klassen die Schnittstellen erfüllen, können zugewiesen werden. Beim Aufruf einer Operation (zur Laufzeit) wird entsprechende Methode ausgewählt (abhängig vom Objekt). • Späte Bindung (dynamisches Binden) Im Hauptprogramm wird zufällig eine Implementierung ausgewählt. Wie wird die richtige Methode gefunden? Zur Laufzeit wird von der JVM abhängig vom momentan zugewiesenen Objekt die entsprechende Methode aufgerufen – dynamisches Binden. Methodenaufruf wird abhängig vom Kontext in unterschiedliche Abläufe umgesetzt. Complex c1 = new Polar (2, 0); Complex c2 = new Cartesian(0, 1); c1.multiply(c2); c2.multiply(c1); Programmiermethodik - Vererbung und Polymorphismus 23 Statischer/Dynamischer Typ Statischer Typ • Typ der Variable laut Deklaration • Bestimmt, welche Objektvariablen und Methoden angesprochen werden können. • Kompatibilitätsüberprüfung Dynamischer Typ • Typ zur Laufzeit (abhängig vom tatsächlich zugewiesenen Objekt) • Kann sich nach jeder (gültigen) Zuweisung ändern. • Er bestimmt, welche Methoden wirklich aufgerufen werden. Programmiermethodik - Vererbung und Polymorphismus 24 Beispiel dynamischer Typ Complex c = new Polar (2, 0); c = new Cartesian(2, 0); c = Math.random() > 0.5 ? c : new Polar(1, 1); c = c.multiply(c); Programmiermethodik - Vererbung und Polymorphismus 25 Mehrfache Implementierung • Eine Klasse kann mehrere Interfaces implementieren (und somit alle Methoden in den Interfaces implementieren). • Mehrere Interfaces können die gleiche Methode vorschreiben (gleiche Signatur). • Die Klasse, die diese Interfaces implementiert, muss die Methode nur einmal implementieren. • Erben mehrerer abstrakter Klassen nicht möglich! Programmiermethodik - Vererbung und Polymorphismus 26 Beispiel (mehrfache Implementierung) public interface Printable { String toString(); } public class Cartesian implements Complex, Printable{ //methods for interface Complex as before public String toString(){ return(this.real + " + " + this.imag + "i"); } } Programmiermethodik - Vererbung und Polymorphismus 27 Beispiele • Stack.java • Container.java • Iterator.java, • ArrayStack.java (ArrayIterator.java) • StackContainerTest.java Programmiermethodik - Vererbung und Polymorphismus 28 Ableiten (bei Interfaces) • Ein Interface kann von einem anderen Interface abgeleitet werden. • Es wird das Schlüsselwort extends verwendet. • Ein Interface kann mehrere Super-Interfaces haben. • Das Interface übernimmt alles aus den Super-Interfaces und kann zusätzliche Methoden und Konstanten hinzugeben. Programmiermethodik - Vererbung und Polymorphismus 29 Beispiel public interface Iterator { boolean hasNext() ; //is there a next element? String next( ) ; // give me next element } public interface RemovableIterator extends Iterator { void remove() ; // remove last element that was returned by next() // from container // automatically inherits methods from Iterator } Siehe auch LinkedList.java • Programmiermethodik - Vererbung und Polymorphismus • 30 Konstanten • In Interfaces können Konstanten deklariert werden. • Kostanten können auch weitervererbt werden. • Wenn eine Klasse ein Interface mit Konstanten implementiert, dann übernimmt sie diese Konstanten! • Wird normalerweise nicht empfohlen! Sollte in einer sauberen Implementierung vermieden werden (Mischung Implementierung und Spezifikation)! Programmiermethodik - Vererbung und Polymorphismus 31 Beispiel (schlecht!) public interface Grundfarben { int ROT = 1, GRUEN = 2, BLAU = 3; } public interface Sockenfarben extends Grundfarben { int SCHWARZ = 10, LILA = 11; } public interface Hosenfarben extends Grundfarben { int LILA = 11, SCHWARZ = 20, BRAUN = 21; } public interface Allefarben extends Sockenfarben, Hosenfarben { int BRAUN = 30; } public class A implements Allefarben { } Ausgabe: 1 1 20 1 1 20 public class InterfaceTest { private InterfaceTest () { } public static void main( final String [] args ) { System.out.println(Sockenfarben.ROT); System.out.println(Allefarben.ROT); System.out.println(Hosenfarben.SCHWARZ); // System.out.println(Allefarben.SCHWARZ); Funktioniert nicht! // System.out.println(Allefarben.LILA); Funktioniert nicht! A a = new A(); System.out.println(a.ROT); System.out.println(A.ROT); System.out.println(((Hosenfarben) a).SCHWARZ); SCHWARZ } } Programmiermethodik - Vererbung und Polymorphismus und LILA sind 2 mal vorhanden! 32 Einsatz von Interfaces • Erlauben die isolierte Entwicklung von Implementierungen. • Anwendungen können nur auf Interfaces aufbauen. Anwendung deklariert Variablen vom Interface-Typ. Zukünftige Klassen, die das Interface implementieren, können sofort in die Anwendung eingebunden werden. • Leere Interfaces Marker-Interfaces (werden noch besprochen) Programmiermethodik - Vererbung und Polymorphismus 33 Vererbung der Implementierung in Java Vererbung und Interfaces • Ein Interface fixiert gemeinsame Eigenschaften von Klassen. Klassen erfüllen den gleichen Zweck (im Interface angegeben). Sind ansonsten unabhängig. • Bei vielen Klassen beruht die Verwandtschaft nicht nur auf gleichen Eigenschaften sondern auf Erweiterung und Modifikation von Eigenschaften. Programmiermethodik - Vererbung und Polymorphismus 35 Vererbung der Implementierung (allgemein) • Unterklassen erben die in den Oberklassen bereits implementierte Funktionalität. • Public und im gleichen Paket • Unterklassen erben Verpflichtungen Alle Methoden Alle Daten Sofern diese zur Schnittstelle der Oberklasse gehören oder durch Sichtbarkeitsregeln freigegeben wurden. • Funktionalität kann komplett übernommen werden oder von der Unterklasse verändert/erweitert werden. Überschreiben Programmiermethodik - Vererbung und Polymorphismus 36 Überschreiben (allgemein) • Unterklasse implementiert eine Operation, für die es bereits in einer Oberklasse eine Methode gibt. • Wird die Methode auf ein Exemplar der Unterklasse aufgerufen, dann wird die überschriebene Implementierung aufgerufen. Hängt nicht vom Typ der Variable ab, über die das Objekt referenziert wird. Entscheidend ist der Typ des Objekts. Polymorphie! • Überschriebene Methode kann eigene Implementierung haben oder zusätzlich auf geerbte Implementierung zugreifen. Programmiermethodik - Vererbung und Polymorphismus 37 Überschreiben (speziell) • Geschützte Methoden (protected) Gehören nicht zur Schnittstelle, nur in Unterklassen und im gleichen Paket sichtbar. Unterstützen Methoden der Schnittstelle. Können in Unterklassen verwendet und überschrieben werden. • Überschreiben verhindern (final) Oberklasse will eine bestimmte Funktionalität erzwingen. Unterklasse darf nicht überschreiben (z.B. um bestimmte Verträge einzuhalten). Programmiermethodik - Vererbung und Polymorphismus 38 Beispiel Fahrzeugverwaltung • KFZ Kennzeichen Standort Methode zur Ausgabe der KFZ-Daten • LKW Kennzeichen Standort Ladung Methode zur Ausgabe der KFZ-Daten Methode zum Ändern der Ladung Programmiermethodik - Vererbung und Polymorphismus 39 Beispiel (schlechte Lösung) • Vehicle1.java • Truck1.java • VehicleTest1.java Programmiermethodik - Vererbung und Polymorphismus 40 Vererbung in Java • Eine Klasse B kann die Subklasse (Unterklasse, abgeleitete Klasse) einer anderen Klasse A (Superklasse, Oberklasse, Basisklasse) sein. • Eine Subklasse erbt alle Objektvariablen und Methoden der Superklasse. • Eine Subklasse kann weitere Objektvariablen deklarieren und Methoden implementieren. • Ein Objekt der Subklasse ist auch ein Objekt der Superklasse. • Java unterstützt nur Einfachvererbung (nur eine Superklasse)! Programmiermethodik - Vererbung und Polymorphismus 41 Beispiel (mit Vererbung) • Vehicle2.java • Truck2.java • VehicleTest2.java Programmiermethodik - Vererbung und Polymorphismus 42 Syntax • Zusatz „extends Vehicle2“ lässt die Klasse Truck2 von der Klasse Vehicle2 erben. • Bestandteile der Klasse Vehicle2 werden in der Klasse Truck2 eingeblendet (sind dort vorhanden). Aber nicht alles ist sichtbar (z.B. number, location sind als private deklariert)! • Methode getInfo wird geerbt und kann in der Klasse Truck2 aufgerufen werden. Programmiermethodik - Vererbung und Polymorphismus 43 Konstruktoren bei der Vererbung • Konstruktoren werden nicht vererbt. • Jeder Konstruktor einer Subklasse muss zuerst einen Superklassen-Konstruktor aufrufen (Initialisierung der Superklasse)! • Wenn nichts angegeben wird, dann ruft ein Konstruktor den Default-Konstruktor in der Superklasse auf. • Wenn dieser nicht existiert? Entweder in der Superklasse implementieren. Einen anderen Konstruktor in der Superklasse explizit aufrufen. Programmiermethodik - Vererbung und Polymorphismus 44 Expliziter Aufruf • Der Default-Konstruktor kann auch explizit mit super(); aufgerufen werden (redundant!). • Wie bei this() und seinen Varianten kann man auch bei super() zu allen möglichen Konstruktoren eine Verbindung herstellen. Im Beispiel wird mit super(name,address); der entsprechende Konstruktor in Vehicle aufgerufen. • Folgende Einschränkungen existieren: Der super-Aufruf darf nur einmal vorkommen. Der super-Aufruf muss als erste Anweisung auftreten. this()- Aufrufe und super()- Aufrufe können nicht gleichzeitig verwendet werden (immer erster Aufruf!). Programmiermethodik - Vererbung und Polymorphismus 45 Zugriffsschutz mit protected • Im Beispiel sind number und location mit private gekennzeichnet. In Truck2 kann man nicht direkt darauf zugreifen. • Mit protected markierte Objektvariablen und Methoden stehen allen Subklassen zur Verfügung. Sind in den Subklassen sichtbar und in Klassen des gleichen Pakets. Sichtbarkeit erstreckt sich auch über mehrere Stufen einer Vererbungshierarchie. Wie bei private gibt es eine Unterscheidung zwischen klassenbasierter und objektbasierter Definition. In Java klassenbasiert! Programmiermethodik - Vererbung und Polymorphismus 46 Überschreiben von Methoden • Eine Klasse kann Methoden, die sie von der Superklasse erbt, neu implementieren (Überschreiben). • Folgende Regeln gelten dabei: Name und Parameterliste der überschriebenen Methode müssen exakt übernommen werden. Ansonsten: Überladen (auch in Subklassen möglich) Der Zugriffsschutz darf gelockert werden (z.B. protected in public). Der Ergebnistyp darf vom Ergebnistyp der zu überschreibenden Methode abgeleitet werden. Der Rumpf kann komplett ersetzt werden. Programmiermethodik - Vererbung und Polymorphismus 47 Beispiele • Zugriff mittels protected Attributen widerspricht Prinzip der Datenkapselung! Vehicle3.java Truck3.java VehicleTest3.java • bessere Lösung mit Zugriff mittels protected Gettern: Vehicle4.java Truck4.java VehicleTest4.java Programmiermethodik - Vererbung und Polymorphismus 48 Zugriff auf die Superklasse • super kann auch in normalen Methoden eingesetzt werden. Wenn man eine Methode nur erweitern möchte. • super.m() ruft die entsprechende Methode m() in der direkten Superklasse auf. • Eine weitere Verkettung ist nicht möglich! d.h. super.super.m() ist nicht möglich. • m() darf nicht private sein. • Kann auch bei Objektvariablen eingesetzt werden. Programmiermethodik - Vererbung und Polymorphismus 49 Beispiel (3. Version) • bessere Lösung mittels super: Vehicle5.java Truck5.java VehicleTest5.java Programmiermethodik - Vererbung und Polymorphismus 50 Polymorphismus (Wiederholung) • Es ist erlaubt, dass einer Variable oA vom Typ A ein Objekt oB einer beliebigen Subklasse B zugewiesen werden darf. oA=oB ist immer erlaubt (Ersetzbarkeitsprinzip). • oB=(B) oA ist nur zulässig, wenn oA auf ein B-Objekt zeigt (wenn der Typ von OA also OB ist). Expliziter Cast notwendig. Wird als Downcast bezeichnet. Downcast ist problematisch! Verletzt Prinzip der Trennung der Schnittstelle von der Implementierung. Kann meist mit Hilfe der dynamischen Polymorphie und dem Prinzip der Ersetzbarkeit umgangen werden. Programmiermethodik - Vererbung und Polymorphismus 51 instanceof - Operator • Objektvariablen sind polymorph. Sie haben einen statischen Typ (Deklaration, z.B. Complex). Sie haben eine aktuelle Klassenzugehörigkeit (dynamischer Typ, z.B. Polar). • Der dynamische Typ kann mit instanceof abgefragt werden. Vererbung muss aber berücksichtigt werden. Erbt eine Klasse B von einer Klasse A, dann ist ein entsprechendes Exemplar der Klasse B auch Exemplar der Klasse A, die Umkehrung gilt aber nicht! instanceof sollte nur in Ausnahmefällen eingesetzt werden. Programmiermethodik - Vererbung und Polymorphismus 52 Beispiel (Polymorphie, Typtest) • VehicleTest5b.java Programmiermethodik - Vererbung und Polymorphismus 53 final bei Methoden und Klassen • Wird eine Methode zusätzlich mit dem Schlüsselwort final versehen, dann kann die Methode in einer Subklasse nicht mehr überschrieben werden und wird dynamisches Binden unterbunden. • Man kann auch eine Klasse mit final versehen. Es kann keine Subklasse gebildet werden. Alle Methoden sind automatisch final. Beispiele (Performance als Grund für final) String, Stringbuffer, StringBuilder, …. Programmiermethodik - Vererbung und Polymorphismus 54 Kovarianz, Kontravarianz, Invarianz • Was darf beim Überschreiben verändert werden? • Kovarianz (entlang der Vererbungsrichtung) Redefinition von Parameter- und Ergebnistypen mit Subtypen • Kontravarianz (entgegen der Vererbungsrichtung) Redefinition von Parameter- und Ergebnistypen mit Supertypen • Invarianz Typen bleiben gleich • Java (seit Version 1.5) Kovarianz bei Ergebnistypen Sonst Invarianz Programmiermethodik - Vererbung und Polymorphismus 55 Kovarianter Ergebnistyp • Kovariante Ergebnistypen erlauben jeden kompatiblen Ergebnistyp bei der Redefinition ererbter Methoden und bei der Implementierung von Interfacemethoden. • Beispiel public class A{ public A get(){...} } public class B extends A{ public B get(){...} } public class C extends B{ public C get(){...} } Programmiermethodik - Vererbung und Polymorphismus 56 Vererbung der Implementierung • Vorteil der Vererbung der Implementierung Methoden müssen nicht neu implementiert werden. Redundanzen im Quellcode werden vermieden. DRY! • Aber Vererbung legt eine starre Struktur fest. Erweiterungen sind mit Aufwand verbunden. Programmiermethodik - Vererbung und Polymorphismus 57 Probleme • Klasse B kann Funktionalität der Klasse A nutzen durch: Vererbung (B erbt von A) Beziehung (Assoziation zwischen B und A) • Falle Vererbung erscheint einfacher. Aber nicht immer sinnvoll. Prinzip der Ersetzbarkeit sollte immer gelten (wo Supperklasse erwartet wird, kann auch Subklasse eingesetzt werden)! Programmiermethodik - Vererbung und Polymorphismus 58 Bsp: Einhaltung des Prinzips der Ersetzbarkeit • Container bietet Einfügen und Iterator an Bedingungen: Iterator muss über alle Elemente iterieren, die bislang eingefügt (und nicht entfernt) wurden • Klasse ArrayStack implementiert Container-Schnittstelle • Klasse LinkedList implementiert Container-Schnittstelle Programmiermethodik - Vererbung und Polymorphismus 59 Verletzung des Prinzips der Ersetzbarkeit public class Rectangle { double width; double height; public void scaleWidth ( double factor) { . . . } public void scaleHeight ( double factor) { . . . } public void scale (double factor) { . . . } . . . } public class Square extends Rectangle { // invariant : width = h e i g h t } • Problem: Quadrat kann nicht wie Rechteck benutzt werden; Beispiel: Rectangle r = new Square(5.0); r.scaleWidth(2); Invariante wird verletzt Programmiermethodik - Vererbung und Polymorphismus scaleWidth() Invariante nicht mehr erfüllt Funktion kann nicht zur Verfügung gestellt werden Prinzip der Ersetzbarkeit? 60 Problem der instabilen Basisklasse • Falls das Prinzip der Ersetzbarkeit gilt? Alles ok? Gilt das auch in der Zukunft? • Problem der instabilen Basisklasse (Fragile Base Class Problem). Anpassungen an der Basisklasse können zu unerwartetem Verhalten von abgeleiteten Klassen führen. Wartung von Systemen, die nur Vererbung der Implementierung benutzen, ist schwierig. • Sind spätere Änderungen an der Basisklasse sehr wahrscheinlich: Vererbung der Spezifikation Wenn man Redundanzen vermeiden möchte - Delegation Programmiermethodik - Vererbung und Polymorphismus 61 Dynamisches Binden • Beim Übersetzen von Methodenaufrufen prüft der Compiler den statischen Typ des Zielobjekts. Die Variable v in typeTest(Vehicle v) hat den Typ Vehicle. Die Klasse Vehicle5 definiert die Methode getInfo() und daher wird der Aufruf akzeptiert. • Zur Laufzeit wird der Methodenaufruf dynamisch gebunden. Der aktuelle Typ von v wird herangezogen um die tatsächliche Methode zu bestimmen. Wenn die Methode nicht überschrieben wurde, wird die entsprechende Methode aus der Superklasse aufgerufen. • Beispiel: typeTest(new Truck5(...)) wird getInfo () aus Truck5 ausführen Programmiermethodik - Vererbung und Polymorphismus 62 Statisches Binden • In bestimmten Situationen werden Methoden statisch (und nicht dynamisch) gebunden. Statische Methoden Gehören zu einer Klasse und nicht zu einem Objekt. Konstruktoren Objekt muss erst vom Konstruktor erzeugt werden. Private Methoden Sind in der Subklasse nicht sichtbar. Eine neue Definition einer privaten Methode ist keine Redefinition! • Binden von Datenelementen Datenelemente werden statisch gebunden. Beim Zugriff wird der statische Typ herangezogen. Datenelemente werden aber geerbt. Programmiermethodik - Vererbung und Polymorphismus 63 Beispiel (statische Datenelemente) public class Base { protected int data = 1; } public class Sub extends Base { protected int data = 2; } Ausgabe: 1 2 public class Sub2 extends Sub { } public class Test { public static void main(final String[] args) { final Base x = new Sub(); final Sub2 s = new Sub2(); System.out.println(x.data); System.out.println(s.data); } } Programmiermethodik - Vererbung und Polymorphismus 64 Delegation • Ein Objekt setzt eine Operation so um, dass der Aufruf der Operation an ein anderes Objekt delegiert (weitergereicht) wird. • Verantwortung, eine Implementierung für eine bestimmte Schnittstelle bereitzustellen, wird an ein Exemplar einer anderen Klasse delegiert. • Vorteil Kann mit der Vererbung der Spezifikation gemeinsam benutzt werden (Quellcode einsparen). Auch ohne Mehrfachvererbung kann die Funktionalität von mehreren Klassen benutzt werden. Dynamisch (Delegat kann zur Laufzeit geändert werden). • Beispiel Brunnen: LoggingWell3.java, WellTest.java Programmiermethodik - Vererbung und Polymorphismus 65 „is-a“ vs. „has-a“ • „B ist ein A“ → B wird von A abgeleitet. • „B enthält ein A“→ B definiert eine Objektvariable vom Typ A. • Beispiel 2 Klassen Dreieck und Punkt (has-a) Dreieck ist ein Punkt – Falsch Dreieck enthält einen Punkt – Richtig Klasse Dreieck verwendet daher die Klasse Punkt (Objektvariable vom Typ Punkt). 2 Klassen Dreieck und Polygon (is-a) Dreieck ist ein Polygon – Richtig Dreieck enthält ein Polygon – Falsch Dreieck wird von Polygon abgeleitet. Programmiermethodik - Vererbung und Polymorphismus 66 Abstraktionsebenen • Vererbung einsetzen, wenn Superklasse und Subklasse konzeptionell auf unterschiedlichen Abstraktionsebenen stehen. Klassenname ist umgangssprachlich ein Oberbegriff des anderen Klassennamens. • Beispiel Polygon Quadrangle Rectangle Programmiermethodik - Vererbung und Polymorphismus Square 67 Abstrakte Klassen Abstrakte Klassen • Konkrete Superklassen und Interfaces bilden zwei Extreme. • Abstrakte Klassen bilden einen Mittelweg. Enthalten Objektvariablen. Enthalten vollständige Methoden. Enthalten Schnittstellen (abstrakte Methoden). • Eine abstrakte Klasse wird mit dem Schlüsselwort abstract markiert. Zusätzlich werden die abstrakten Methoden mit abstract gekennzeichnet. Es kann auch keine abstrakten Methoden in einer abstrakten Klasse geben. Wenn eine Klasse ein Interface nicht vollständig implementiert, dann muss sie auch eine abstrakte Klasse sein. Programmiermethodik - Vererbung und Polymorphismus 69 Vererbung bei abstrakten Klassen • Von einer abstrakten Klasse können keine Objekte angelegt werden. • Eine Subklasse muss alle abstrakten Methoden implementieren, damit man von dieser Subklasse Objekte anlegen kann. • Implementiert eine Subklasse nur einen Teil (oder keine) der abstrakten Methoden, dann ist sie auch eine abstrakte Klasse. • In einer Subklasse können neue Methoden definiert und Methoden überschrieben werden. • Auch super kann verwendet werden. • Beispiel Siehe nächste Folie Programmiermethodik - Vererbung und Polymorphismus 70 Abstrakte Klassen public abstract class DefaultStack implements Stack { // provide default implementation for peek public Object peek() { Object o = this.pop(); this.push(o); return o; } // leave remaining stack operations open // for concrete classes public abstract Object pop(); public abstract boolean isEmpty(); public abstract void push(Object obj); } Programmiermethodik - Vererbung und Polymorphismus 71 Abstrakte Klasse - Interface • Unterschiede Die abstrakten Methoden können auch den Zugriffsschutz protected und default haben (in einem Interface ist nur public erlaubt). Können Objektvariablen definieren (nicht nur Konstanten). Können Konstruktoren enthalten, die von den Subklassen benutzt werden. • Rein abstrakte Klassen sind möglich. Keine Objektvariablen Nur abstrakte Methoden • Wozu dann Interfaces? In Java gibt es bei Klassen nur Einfachvererbung! Programmiermethodik - Vererbung und Polymorphismus 72 Subtyping und Subclassing Subtyping • Wenn eine Klasse ein Interface implementiert (von diesem Interface „erbt“). • Es wird nur der Typ „geerbt“ Subclassing • Wenn eine Klasse von einer andere Klasse erbt. • Es wird die Implementierung geerbt. Programmiermethodik - Vererbung und Polymorphismus 73 Varianten der Vererbung (Zusammenfassung) Reine Vererbung der Spezifikation • Schnittstellen-Klassen (Interfaces in Java) Vererbung der Spezifikation und Implementierung • Normalfall (auch in Java) • Unterklasse erbt Spezifikation und Implementierung. Reine Vererbung der Implementierung • Unterklasse übernimmt nur Implementierung, nicht die Spezifikation. • Für interne Nutzung (z.B. private Vererbung in C++) Programmiermethodik - Vererbung und Polymorphismus 74 Die Klasse Object Die Wurzelklasse Object • Jede Klasse, die von keiner Klasse erbt, erbt automatisch von der Wurzelklasse Object. Folgende Definitionen sind äquivalent: public class className {…} public class className extends Object {…} • Damit werden alle Klassen direkt oder indirekt von Object abgeleitet. Programmiermethodik - Vererbung und Polymorphismus 76 Vordefinierte Methoden (1) • Object bietet einige Methoden an, die an jede Java-Klasse vererbt werden. • Beispiele (vereinfacht !) public String toString() Liefert eine lesbare Repräsentation des Objekts (siehe getInfo() im Beispiel). public boolean equals(Object x) Prüft ob das Zielobjekt und x gleich sind. public int hashCode() … Liefert eine Kennnummer (Hashcode) des Objekts. protected Object clone() Liefert eine Kopie des Objekts. protected void finalize() … Wird vom Garbage Collector aufgerufen. Problematisch Programmiermethodik - Vererbung und Polymorphismus 77 Vordefinierte Methoden (2) • Methoden (gesamte Information, wird in den folgenden Vorlesungen noch teilweise erklärt): public final native Class<?> getClass(); public native int hashCode(); public boolean equals(Object obj) { ... } protected native Object clone() throws CloneNotSupportedException; public String toString() { ... } public final native void notify(); public final native void notifyAll(); public final native void wait(long timeout) throws InterruptedException; public final void wait(long timeout, int nanos) throws InterruptedException { ... } public final void wait() throws InterruptedException { ... } protected void finalize() throws Throwable { ... } Programmiermethodik - Vererbung und Polymorphismus 78 Implementierungen in der Klasse Object • Minimale Implementierung • Die Methoden sollten in jeder Klasse entsprechend überschrieben werden. • Wenn diese Methoden nicht überschrieben werden, wird die minimale Implementierung aus Object verwendet. • Beispiele toString gibt den Namen der Klasse zusammen mit dem Hashcode aus: public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } equals vergleicht nur die Referenzen: public boolean equals(Object obj) { return (this == obj); } usw. Programmiermethodik - Vererbung und Polymorphismus 79 equals • Default-Implementierung vergleicht nur Referenzen. • Eine korrekte Implementierung erfüllt: Reflexivität x.equals(x) → true Symmetrie x.equals(y) == y.equals(x) Transitivität x.equals(y) → true, y.equals(z) → true dann gilt auch x.equals(z) → true Konsistenz Zwei Objekte müssen bei wiederholten Aufrufen von equals immer das gleiche Ergebnis liefern, so lange sie sich nicht verändert haben. Für alle Objekte x, die nicht gleich null sind, gilt: x.equals(null) → false Programmiermethodik - Vererbung und Polymorphismus 80 Probleme bei instanceof • Zwei Objekte voneinander abgeleiteter Typen liefern kein symmetrisches Ergebnis: Person p = new Person("Willi","Weg 3"); Student s = new Student("Maja","Allee 2",200); System.out.println(s instanceof Person);// true System.out.println(p instanceof Student); // false Programmiermethodik - Vererbung und Polymorphismus 81 Typobjekte • Typobjekte Java Reflection – siehe Entwurf von Softwaresystemen. • Jedem Typ ist ein eindeutiges Typobjekt zugeordnet. • Type.class System.out.println(String.class) liefert „class java.lang.String“ • Das Typobjekt eines Objekts obj kann mit obj.getClass() ermittelt werden (auch eine Methode der Klasse Object). System.out.println("Hello".getClass()); Liefert „class java.lang.String“ Programmiermethodik - Vererbung und Polymorphismus 82 hashcode • Für jedes Objekt sollte eine eindeutige Kennnummer (nicht immer möglich) produziert werden. • Wird vor allem im Collection-Framework (z.B. HashMap; wird noch besprochen) benutzt. • Anforderungen: Hashcode muss immer gleich bleiben, solange sich das Objekt nicht ändert. Wenn zwei Objekte gemäß equals gleich sind, müssen sie auch den gleichen Hashcode produzieren. Die Umkehrung gilt nicht, d.h. zwei Objekte können den gleichen Hashcode haben, aber verschieden sein. Programmiermethodik - Vererbung und Polymorphismus 83 hashcode (Berechnung) • Integervariable result mit einem Wert > 0 belegen. • Für jede Objektvariable a (die auch von equals verwendet wird) im Objekt berechne abhängig vom Typ: boolean: (a ? 1 : 0) byte, char, short, int: (int) a long: (int) (a^(a>>>32)) float: Float.floatToIntBits(a) double: Double.doubleToIntBits(a) Objektreferenzen: Wenn null dann 0, ansonsten Aufruf von hashcode(). Array: Benutze Arrays.hashcode() oder behandle jedes Element wie eine eigene Objektvariable. • Nachdem für eine Objektvariable der Hashcode c berechnet wurde, berechne (unter Benützung von prime, prime meist 31). result = prime*result+c • Gib result zurück. Programmiermethodik - Vererbung und Polymorphismus 84 hashcode und equals • equals und hashCode hängen zusammen und sollten immer gemeinsam überschrieben werden. • equals und hashCode sollten immer die gleichen Objektvariablen verwenden. • Man sollte immer die hashCode-Methode von Referenzen verwenden (falls implementiert). Programmiermethodik - Vererbung und Polymorphismus 85 clone • Ein Kopier-Konstruktor eignet sich zum Kopieren eines Objekts ohne Vererbung. • Kopieren bei einem dynamischen Typ kann nur durch eine dynamisch gebundene Methode erfolgen – clone-Methode. • clone hat den Zugriffsschutz protected. Damit kann die Methode nicht von außen aufgerufen werden, wenn sie nicht überschrieben wird. Beim Überschreiben muss der Zugriffsschutz gelockert werden (public). Programmiermethodik - Vererbung und Polymorphismus 86 Regeln für clone • Eine Klasse muss das Interface Cloneable implementieren, eine eigene öffentliche Methode clone() implementieren, in clone eine Kopie des Superklassenobjekts mit super.clone() erzeugen, in clone alle Datenelemente veränderlicher Klassen einzeln mit clone-Aufrufen auf diese Objekte kopieren. • clone kann nur verwendet werden, wenn jede verwendete Klasse clone korrekt implementiert. • Beispiele folgen noch. Programmiermethodik - Vererbung und Polymorphismus 87