Loading presentation...

Present Remotely

Send the link below via email or IM

Copy

Present to your audience

Start remote presentation

  • Invited audience members will follow you as you navigate and present
  • People invited to a presentation do not need a Prezi account
  • This link expires 10 minutes after you close the presentation
  • A maximum of 30 users can follow your presentation
  • Learn more about this feature in our knowledge base article

Do you really want to delete this prezi?

Neither you, nor the coeditors you shared it with will be able to recover it again.

DeleteCancel

Polymorphie

No description
by

Lars Praedel

on 17 December 2017

Comments (0)

Please log in to add your comment.

Report abuse

Transcript of Polymorphie

Hochschule Bremen
Dr. Lars Prädel
WS 2017/2018
Programmieren 1
– Polymorphie –

Polymorphie („Vielgestaltigkeit“) ist ein grundlegendes Konzept der Objektorientierung:

Ein Objekt einer Klasse (PKW) kann mehrere Erscheinungsformen annehmen (PKW, Kfz)

Eine Referenz (Kfz) kann ein Objekt der Klasse selbst oder ein Objekt der Unterklasse (LKW, PKW) aufnehmen


Hintergrund:

Klassen können wie Typen verwendet werden
Referenz vom Typ einer Oberklasse (polymorphe Variable) kann auch auf Objekte der Unterklassen verweisen
(-> siehe Regeln für implizite Typumwandlung)
Generell gilt: Ein Objekt einer Unterklasse kann überall dort verwendet werden, wo ein Objekt der Oberklasse erlaubt ist (aber nicht umgekehrt!)
es werden immer die richtigen Methoden aufgerufen
Polymorphie

Idee:

Führe neue Klassen PKW und LKW ein, die von der bereits bestehenden Klasse Kfz abgeleitet sind


Wegen Subtyp-Beziehung zwischen PKW und Kfz bzw. LKW und Kfz kann Kfz-Referenz auch auf PKW- bzw. LKW-Objekte zeigen


Typ des Objekts (d.h. die Klasse), auf den die Kfz- Referenz tatsächlich verweist, bestimmt den auszuführenden Code (z.B. Wartungsmethode aus PKW, LKW oder Oberklasse Kfz)
Zugriffsrechte: Überschreiben von Methoden
baujahr
nummernschild
ladeflaeche
warten()
beladen()
entladen()
baujahr
nummernschild
ladeflaeche
warten()
beladen()
entladen()
Zugriffsmodifikatoren in Superklasse
Bei den in abgeleiteten Klassen überschriebenen Methoden dürfen die Zugriffsrechte ausschließlich erweitert werden:
Vererbung
Grundlegendes Konzept der Objektorientierung
Klassen stehen zueinander in einer ist-ein (is-a) Beziehung
Jeder PKW ist ein Kraftfahrzeug
Jedes Kraftfahrzeug ist ein Transportmittel
Jeder LKW ist ein Kraftfahrzeug (und damit auch ein Transportmittel)
PKW
- maxPassagiere : int
+ kofferraumOeffnen()
LKW
- ladeflaeche : double
+ beladen()
+ entladen()
Bus
- maxPassagiere : int
public class Kfz {
protected int baujahr; // Sichtbarkeit für Unterklassen
protected String nummernschild;

public Kfz(int bj, String nr) {
this.baujahr = bj;
this.nummernschild = nr;
}
public void warten() {...}
}

public class PKW extends Kfz {
private int maxPassagiere;
// alle Attribute und Methoden werden übernommen
public PKW(int bj, String nr, int p){
super(bj, nr); // Aufruf Konstruktor der Oberklasse
this.maxPassagiere = p;
}
public void kofferraumOeffnen() {...}
}





public class LKW extends Kfz {
private Ladeflaeche flaeche;
// alle Attribute und Methoden werden übernommen
public LKW(int bj, String nr, Ladeflaeche lf){
super(bj, nr);
this.ladeflaeche = lf;
}

public void warten() {
flaeche.leeren();
super.warten();
}
public void beladen() {...}
public void entladen() {...}
}



public class Fuhrpark {

Kfz[] kfzListe = new Kfz[3];
...
public static void main(String[] args) {
// Fahrzeugbestand definieren
kfzListe[0] = new PKW("HB-A-1");
kfzListe[1] = new LKW("HB-B-2“, 20);
kfzListe[2] = new PKW("HB-C-3");

// Alle Fahrzeuge einmal überholen:
for (int i=0; i<kfzListe.length; i++){
kfzListe[i].warten();
}
}
}
Kraftfahrzeug
+ warten()
- baujahr : int
- nummernschild : String
Beispiel: Erweiterung des Fuhrparks

Fuhrpark führt Fahrzeuge bislang allgemein als Kfz

Objektvariable vom Typ Kfz verweist auf Fahrzeuge

Künftig sollen bei Kfz grundsätzlich PKW und LKW (evtl. auch weitere) unterschieden werden können

Einführung von neuen Klassen PKW und LKW mit speziellen Methoden für die Wartung von PKW bzw. LKW

Lösungsansatz 1
für Erweiterung des Fuhrparks: Typumwandlung (Casting) „von Hand“

Kfz k1;
... ;
((LKW) k1).wartenLKW();

-> Unsicher, Programmierer muss aufpassen

Lösungsansatz 2:
Exakten Typ abfragen (instanceof) und alternativen Code (if ...) ausführen

Kfz k1;
...;
if (k1 instanceof Pkw)
k1.wartenPkw();
else if (k1 instanceof Lkw)
k1.wartenLkw();

-> umständlich (if- Abfragen, Kfz- Klasse muss beide Wartungsmethoden kennen), nicht einfach erweiterbar (weiterer else-if-Block für zB Busse))
Beispiel: Grafikprogramm mit grafischen Primitiven wie Dreieck, Rechteck und Kreis

Definition von Klassen Dreieck, Rechteck und Kreis

Gemeinsamkeiten der grafischen Primitive:
grafische Primitive haben eine Farbe und einen Mittelpunkt
grafische Primitive können gezeichnet werden

-> Generalisierung:
Definition einer allgemeinen Basisklasse GrafikObjekt für oben genannte Klassen

Problem:
Implementierung von zeichnen() in GrafikObjekt?
Abstrakte Klassen
Einschränkung des nutzbaren Protokolls
Bsp.:

LKW l = new LKW();
Kfz k = l;
k.warten(); // geht: Typ Kfz hat Methode warten()
k.beladen(); // geht nicht: k vom Typ Kfz


Vom Typ der Referenz abhängige Sicht auf ein Objekt (hier LKW)
Schnittstellen entsprechen abstrakten Klassen, die ausschließlich

Konstanten und
abstrakte Methoden

enthalten, also keine Instanz-Attribute

Beispiel:

public interface MitHupe {

public static final int DB = 110;
String SOUND1 = “Tuuuuut”;
String SOUND2 = “Määääp”;

public void hupen();
}

Schnittstellen eignen sich insbesondere, um ein Protokoll (d.h. eine Menge von Methoden) festzulegen, ohne bereits eine Implementierung angeben zu müssen
Schnittstellen: Definition
Schnittstellen und abstrakte Klassen stellen unvollständig definierte ReferenzTypen dar
d.h. Es können Referenzvariablen definiert werden, die auf Objekte von Klassen zeigen, die die Schnittstelle implementieren (MitHupe h = new LKW();)

Überlegungen zu Typumwandlungen bei Klassen gelten daher analog auch für abstrakte Klassen und Schnittstellen

Wichtig: Mit Hilfe von Schnittstellen kann Mehrfachvererbung „vorgetäuscht“ werden

Erinnerung: In Java ist es nicht möglich, eine Klasse von mehreren Oberklassen abzuleiten (extends)
Implementierung mehrerer Schnittstellen (implements) stellt sicher, dass resultierende Objekte die Eigenschaften aller implementierten Schnittstellen aufweisen
Schnittstellen und abstrakte Klassen: Typen
Klasse ist abstrakt (sobald mind 1 Methode abstrakt ist)
Konstanten müssen initialisiert werden,
“static final“ kann hier auch weggelassen werden, wird vom Compiler autom. ergänzt
Schlüsselwort „public“ und „abstract“ optional, werden autom. vom Compiler hinzugefügt
instanceof ist Standard- Operator in Java
(prüft „is-a“-Beziehungen)
PKW hat keine spezifische warten Methode, sondern erbt die von Kfz
Alle Objekte sind Kraftfahrzeuge

Einige sind besondere (spezialisierte) Arten von Kraftfahrzeugen.

Die Mengen der PKWs, LKWs, Motorräder sind jeweils eine Teilmenge der Menge der Kraftfahrzeuge

Die Teilmengen sind disjunkt
Wenn die Klasse PKW von Kfz abgeleitet ist, so kann man die Objekte der Klasse PKW auch als Objekte der Klasse Kfz betrachten (wenn man die zusätzlichen Attribute und Methoden ignoriert).


Kfz k1 = new PKW(2001, „HB-UX-765“, 5);


Eine Referenzvariable kann auf verschiedene Typen zeigen

Referenzvariable k1 ist vom Typ Kfz

Zeigt aber auf PKW

Jeder PKW ist ein Kfz (weil PKW von Kfz abgeleitet ist!)

ABER: nicht jedes Kfz ist ein PKW !
Fuhrpark
PKW pkw = new PKW("HB-PR-123");
LKW lkw = new LKW();
Kfz kfz1 = new PKW("HB-AA-44");
Kfz kfz2 = lkw;

pkw.warten(); // warten aus Kfz (PKW erbt warten() Methode von Kfz)
kfz2.warten(); // warten aus LKW (Polymorphie)
kfz1.warten(); // warten aus Kfz
kfz2.beladen(); // geht nicht, kfz2 ist Typ Kfz


Zentrale Konzepte hinter Polymorphie: Überschreiben und spätes Binden

Überschreiben („Overriding“):
(Re-)Definition geerbter Methoden ermöglicht polymorphes Verhalten

Spätes/Dynamisches Binden („Late Binding“)

Genauer Typ einer Referenz (einer polymorphen Variablen) steht erst zur Laufzeit fest, d.h. die Referenz wird erst spät an ein Objekt „gebunden“

Hinter dem Namen einer Operation können somit abhängig vom Kontext verschiedene Bedeutungen (Methoden) stehen

Wichtig: Polymorphie schränkt das nutzbare Protokoll auf das Protokoll der Objektreferenz ein (unabhängig vom Protokoll des referenzierten Objekts)
Kfz k = l;
Sicht einer Referenz vom Typ LKW
Sicht einer Referenz vom Typ Kfz
Man kann nicht vorhersagen, welche Methode warten() in folgender Methode aufgerufen wird:

public void fahrtBeenden( Kfz fahrzeug ) {
...
fahrzeug.warten();
...
}


Zeigt fahrzeug auf ein Objekt der Klasse PKW, so wird die (von Kfz geerbte) warten() Methode aufgerufen

Zeigt fahrzeug auf ein Objekt der Klasse LKW, so wird die speziellere warten() Methode von LKW aufgerufen

Die Entscheidung, welche Methode gewählt wird, kann nicht zur Compilezeit getroffen werden, sondern erst zur Laufzeit.

Bei Instanzmethoden bestimmt der Typ des Objektes und nicht der Typ der Referenz (hier Kfz), welche Instanzmethode der Klassenhierarchie aufgerufen wird
Polymorphie: Vorteile
Einfache Erweiterbarkeit von existierendem Code zB. Von neuen Typen (Klassen)

Dabei Nutzung von bereits definierten Objektvariablen

Bestehender Code muss nicht verändert werden (nur für die Objekterzeugung)

Frage: Was ist zu tun, um den Fuhrpark um Motorräder zu erweitern?

Neue Klasse Motorrad von Klasse Kfz ableiten Bei Bedarf ...
... Methoden von Kfz überschreiben
... zusätzliche Methode hinzufügen

Motorrad-Objekte erzeugen und ggf. Kfz-Variablen zuweisen
Kfz-Variablen benutzen wie im Code zuvor, der „Inhalt“ (=das Objekt in der Kfz-Variable) wird die richtigen Dienste aufrufen.
private
<default>
protected
public
Zugriffsmodifikatoren in Subklasse
Überschreiben nicht möglich, aber neue Definition
<default>
protected
public
protected
public
public
Hintergrund:
Zugriff darf in Unterklassen nicht eingeschränkt werden, um Polymorphie zu ermöglichen
Oft anzutreffende Situation in der Softwareentwicklung:

In einer allgemeinen Basisklasse wird die Schnittstelle (das „Protokoll“, d.h. die Menge zur Verfügung gestellter Operationen) für Objekte abgeleiteter Klassen festgelegt

Aber: Basisklasse ist so allgemein, dass noch nicht alle Methoden der Schnittstelle sinnvoll implementiert werden können


Lösung: Abstrakte Methode

Deklaration einer Methode ohne Implementierung
Abstrakte Methoden
Abstrakte Klassen sind unvollständig implementiert:
Mindestens eine Methode ist nicht realisiert, d.h. nicht implementiert

-> Abstrakte Methode

public abstract class GrafikObjekt {
protected String farbe;
protected Punkt mittelpunkt;
...
public void verschieben(int x, int y){...}

public abstract void zeichnen();
...
}

Erzeugung von Instanzen (Objekten) abstrakter Klassen nicht möglich (also nie: new AbstrKl(); )

Es kann aber mit Referenzen vom Typ der abstrakten Klasse gearbeitet werden
Erzeugung von Objekten erfordert vollständig implementierte Klassen
-> Unterklassen müssen abstrakte Methoden realisieren

public class Kreis extends GrafikObjekt {
public void zeichnen() {
// Kreis zeichnen
}
}

public class Rechteck extends GrafikObjekt {
public void zeichnen() {
// Rechteck zeichnen
}
}

GrafikObjekt o1 = new Rechteck();
GrafikObjekt o2 = new Kreis();
o2.zeichen();
Falls Unterklasse B nicht alle Methoden der abstrakten Oberklasse A implementiert, muss diese auch als abstract gekennzeichnet werden
-> Es kann dann aber keine Instanz der Unterlasse B erzeugt werden (da abstract)!
Klassen, die das durch eine Schnittstelle spezifizierte Protokoll anbieten wollen, müssen die Schnittstelle implementieren
Beispiel:
public class LKW extends Kfz implements MitHupe {
...
public void hupen() {
IO.println("Huuuuup!");
}
}
Eine Klasse kann mehrere Schnittstellen implementieren
(durch Kommata getrennt: ... implements MitHupe, MitAnhaengerkupplung)

Dadurch eignen sich Schnittstellen, um verschiedene Dienstangebote in einer Klasse „zusammenzumischen“

Implementiert eine Klasse eine Schnittstelle (Interface), muss sie alle durch die Schnittstelle vorgegebenen Methoden implementieren

Tut sie dies nicht, muss sie als abstract gekennzeichnet werden
-> Erzeugung einer Instanz dieser Klasse dann nicht möglich!
Implementierung
Wird ein Interface in einer Oberklasse implementiert, erben die Unterklassen das verhalten

Ein Objekt der Unterklasse kann dann auch einer Referenzvariablen vom Typ des Interface zugewiesen werden

public class LKW extends Kfz implements MitHupe {
...
public void hupen() {
IO.println("Huuuuup!");
}
}

public class ZweiAchser extends LKW {
...
}

// irgendwo (z.B. main() oder in Methode einer anderen Kasse:)
MitHupe h = new ZweiAchser();

Klasse ZweiAchser erbt Methode hupen() aus LKW, ist daher „MitHupe“
Vererbung an Subklassen
Polymorphie: Motivation
Methode warten() aus Klasse LKW überschreibt gleiche Methode aus Klasse Kfz mit speziellerer Variante
Hier passiert das kleine „Wunder“: Es wird automatisch die richtige warten()-Methode aufgerufen
Offensichtlicher Vorteil: Benutzung von Objekten der neuen Klassen ohne explizite Nennung der Klassen im Code (Ausnahme: Objekterzeugung)
Dynamische Bindung
abstrakte Methode
keine Implementierung!
Klasse Kreis weiß, wie seine Objekte dargestellt werden sollen, und implementiert abstrakte Methode zeichnen()
Schlüsselwort implements zeigt
hier an, dass die Klasse LKW die Schnittstelle MitHupe implementiert
GrafikObjekt
Dreieck
Rechteck
Kreis
Full transcript