Objektorientierte Programmierung OOP
KE4 Vererbung; Begriffe, Beispiele und Definitionen
KE4 Vererbung; Begriffe, Beispiele und Definitionen
Set of flashcards Details
Flashcards | 94 |
---|---|
Language | Deutsch |
Category | Computer Science |
Level | University |
Created / Updated | 28.02.2013 / 18.05.2022 |
Weblink |
https://card2brain.ch/box/objektorientierte_programmierung_oop6
|
Embed |
<iframe src="https://card2brain.ch/box/objektorientierte_programmierung_oop6/embed" width="780" height="150" scrolling="no" frameborder="0"></iframe>
|
Create or copy sets of flashcards
With an upgrade you can create or copy an unlimited number of sets and use many more additional features.
Log in to see all the cards.
Bedeutung: Subtypenbildung bei Schnittstellen
Bekanntlich ist jede Klasse ein Subtyp in einer Klassenhierarchie, entweder ein direkter Subtyp der Klasse Object, oder Subtyp einer spezielleren Klasse wie die Spezialisierungen der Klasse Artikel, die wir schon mehrfach als Beispiele verwendet haben. Eine Klasse, die eine Schnittstelle implementiert, ist zusätzlich auch ein Subtyp dieser Schnittstelle, was wir mit dem instanceof-Operator leicht überprüfen können:
public class A {}
public interface B {}
public interface BB {}
public interface BBB extends B, BB {}
public class C extends A implements BBB {
public static void main(String[] args) {
C myC = new C();
System.out.println(myC instanceof A
&& myC instanceof B
&& myC instanceof BB
&& myC instanceof BBB
&& myC instanceof Object); // prints true
}
}
Mehrfachvererbung durch Schnittstellen lässt sich nutzen, um Klassen mit Eigenschaften auszustatten, die nicht entlang der Klassenhierarchie organisiert sind.
Beispiel: Verderbliche Waren in der Artikelhierarchie
Betrachten wir noch einmal die verschiedenen Artikelgruppen eines Blumenladens. Es liegt nahe, zunächst zwischen Pflanzen und Zubehör zu unterscheiden. Pflanzen können weiter in Topfpflanzen, Schnittblumen, Trockenblumen usw. aufgeteilt werden, Zubehör lässt sich beispielsweise in Gefäße, Pflegemittel, Schmuck usw. auffächern. Eine solche Hierarchie ist sinnvoll gewählt, weil durch Spezialisierung die relevanten Eingenschaften konkreter Artikel präzise abgebildet werden können. Nun kann es aber Eigenschaften geben, die an mehreren Stellen der Hierarchie relevant sind, jedoch nicht durchgehend in einem einzelnen Zweig auftreten. Als Beispiel betrachten wir den Sachverhalt, dass gewisse Waren verderblich sind und deshalb bis zu einem festgelegten Zeitpunkt verkauft sein sollen, während andere Waren fast beliebig lange im Lagerbestand verbleiben können. Beliebig lange haltbar sind Trockenblumen, Gefäße und Schmuck, während Topfpflanzen, Schnittblumen und Pflegemittel nur begrenzt haltbar sind. Um einen Lagerbestand Artikel[] bestand; nach verderblichen Waren abfragen zu können, benötigen wir zweierlei:
• Wir müssen erkennen können, welche Artikel begrenzt haltbar sind.
• Wir wollen das Haltbarkeitsdatum dieser Artikel auf einheitliche Weise erfragen können.
Zu diesem Zweck definieren wir eine Schnittstelle BegrenztHaltbar mit einer Methode liefereHaltbarkeitsdatum(). Klassen, die verderbliche Waren repräsentieren, implementieren die Schnittstelle BegrenztHaltbar und damit die Methode liefereHaltbarkeitsdatum(). Der Lagerbestand kann sodann wie folgt abgefragt werden:
for (Artikel artikel : bestand) {
if (artikel instanceof BegrenztHaltbar) {
// ...
// verwende
// ((BegrenztHaltbar) artikel).lieferHaltbarkeitsdatum()
}
}
Die Abbildung zeigt die Artikelhierarchie mit der Schnittstelle BegrenztHaltbar. Beachten Sie, dass beliebig viele Klassen und Zweige der Artikelhierarchie die Schnittstelle implementieren können, während benachbarte Klassen und Zweige davon unberührt bleiben.
Bemerkung: Mehrfachvererbung und Vererbungskonflikte
In Java tritt Mehrfachvererbung nur im Zusammenhang mit Schnittstellen auf. Von Schnittstellen erbt eine Klasse weder Exemplarvariablen noch Methodenimplementierungen. Andere Programmiersprachen wie Common Lisp oder C++ kennen Mehrfachvererbung auch unter Klassen, was zur Folge haben kann, dass eine Unterklasse mehrere Exemplarvariablen a des gleichen Typs t oder mehrere unterschiedlich implementierte Methoden m() mit der gleichen Signatur von verschiedenen Oberklassen
erbt. Wir nennen dies einen Vererbungskonflikt, weil in solchen Fällen unklar ist, auf welche Definition des Attributs sich ein Zugriff auf a bezieht oder welche Implementierung bei einem Aufruf von m() gemeint ist. Um solche Vererbungskonflikte zu vermeiden, gelten in Programmiersprachen mit Mehrfachvererbung entweder feste Regeln zur Auflösung von Vererbungskonflikten - etwa durch die Vorgabe einer Erbreihenfolge - oder ein Verbot von Vererbungskonflikten. Falls in Java eine Klasse mehrere Schnittstellen implementiert, die Methoden mit gleicher Signatur definieren, so entsteht kein Vererbungskonflikt, denn es erfolgt nur eine einzige Methodenimplementierung in der Klasse selbst. Treten Attribute gleichen Namens in verschiedenen Schnittstellen auf, die von einer Java-Klasse implementiert werden sollen, so müssen sie innerhalb der Klasse mit ihren qualifizierten Namen angesprochen werden, anderenfalls weist der Java- Übersetzer das Programm zurück [JLS: § 9.3.2.1]. Da es sich bei in Schnittstellen definierten Attributen immer um Klassenattribute handelt, ist ihr qualifizierter Name Schnittstellenname.Attributname.Bemerkung: Unterschied zwischen String-Objekten und StringBuilder
Der Unterschied zwischen den beiden Klassen besteht darin, dass String-Objekte Zeichenketten als Konstanten behandeln, während StringBuilder-Objekte veränderliche Zeichenketten verwalten.
Eine Folge von Zeichen, die in Anführungsstrichen eingeschlossen sind, wie z. B. "Der höchste Berg Javas ist der Vulkan Semeru." erzeugt in Java automatisch ein Objekt der Klasse String, dessen Wert mit der Zeichenfolge übereinstimmt. Ein auf diese Weise erzeugtes String-Objekt wird String-Literal genannt. [JLS: § 3.10.5]
Aufgabe: Wie können in Java String und Verkettungen erzeugt werden?
String nachname = "Müller";
String vorname = new String("Heinz");
char[] letters = {’H’, ’a’, ’g’, ’e’, ’n’}; String city = new
String(letters); "Java-" + "Kaffee"
Begriff: Konkatenation
int glueckszahl;
...
System.out.println("Ihre persönliche Glückszahl lautet " + glueckszahl + ".");
Wenn glueckszahl den Wert 17 aufweist, erscheint die Ausgabe:
Ihre persönliche Glückszahl lautet 17.
Bemerkung: Automatische Umwandlung in eine Zeichenkette
Wird ein String-Objekt mit dem Verkettungsoperator mit einem Objektverweis verknüpft, so wird automatisch an diesem Objekt die toString()- Methode aufgerufen und so eine passende Zeichenkettenrepräsentation erzeugt. [JLS: § 15.18.1.1]
Aufgabe: Nennen Sie einige Methoden aus der Klasse String
int laenge = vorname.length(); // liefert 5
int ePosition = vorname.indexOf(’e’); // liefert 1
int inPosition = vorname.indexOf("in"); // liefert 2
char viertesZeichen = vorname.charAt(3); // liefert ’n’
if (vorname.indexOf(’o’) == -1) {
System.out.println("In " + vorname + " kommt kein ’o’
vor.");}
boolean eiKommtVor = vorname.contains("ei");
String vollerName = vorname.concat(nachname);
String neuerName = vorname.replace(’z’, ’o’);
String ausschnitt = vorname.substring(1, 4) // ergibt "ein"
String allesklein = vorname.toLowerCase() // ergibt "heinz"
String allesgross = vorname.toUpperCase() // ergibt "HEINZ"
Bedeutung: Strings vergleichen
Neben Methoden zur Untersuchung der Zeichenketten enthält die Klasse String auch Methoden, um Zeichenketten miteinander zu vergleichen. Dabei fordern wir jeweils ein String-Objekt auf, sich mit einem anderen String-Objekt zu vergleichen. Seien
String s = "neben";
String t = "einander";
String u = "nebeneinander";
String w = s + t;
Die Methode equals() liefert true, falls zwei Zeichenketten lexikalisch gleich sind, ansonsten false:
boolean istGleich;
istGleich = s.equals(t); // liefert false
istGleich = u.equals(s + t); // liefert true
istGleich = u.equals(w); // liefert true
Die Methode equals() ist uns als Methode der Klasse Object bereits begegnet. Auch die Klasse String ist eine Subklasse von Object; durch Überschreiben der equals()-Methode definiert sie die Gleichheit unter String-Objekten um.
Bedeutung: compareTo() Methode
Mit der Methode compareTo() erhalten wir ein differenzierteres Ergebnis. Sie liefert eine Ganzzahl, wobei drei Fälle auftreten können:
• Das Ergebnis 0 bedeutet, dass die beiden Zeichenketten lexikalisch gleich sind (und entspricht somit dem Fall, dass equals() true liefert).
• Eine negative Ganzzahl bedeutet, dass die Zeichenketten des StringObjekts, dessen compareTo-Methode aufgerufen wurde, alphabetisch vor der Zeichenkette des Parameters steht.
• Eine positive Ganzzahl signalisiert die umgekehrte Reihenfolge.
int vergleich;
vergleich = u.compareTo(w); // liefert 0
vergleich = t.compareTo(s); // liefert -9
vergleich = s.compareTo(t); // liefert 9
Bemerkung: Vergleichen von Strings
Gelegentlich werden Ihnen Vergleiche der folgenden Art begegnen:
if (u == w) { ... }
u und w sind die Namen von String-Objekten und stellen somit Referenzen auf diese Objekte dar. Der Vergleichsoperator == prüft, ob u und w auf dasselbe Objekt verweisen. Das Ergebnis dieses Vergleichs ist hier false - unabhängig davon, dass u.equals(w) true ergibt.
In gewissen Fällen kann ein Vergleich von String-Objekten mit dem Vergleichsoperator == auch zu überraschenden Ergebnissen führen:
boolean erstaunlich = (s == "neben"); // liefert true
Dieser Effekt ist dadurch zu erklären, dass die Laufzeitumgebung mit einer internen Optimierung arbeitet und versucht, die Aufbewahrung mehrerer gleicher Zeichenketten im Speicher zu vermeiden. In diesem Fall hat sie für "neben" gar nicht erst ein neues String-Objekt erzeugt, sondern lediglich eine interne Referenz angelegt, die auf das bereits im Speicher existierende String-Objekt mit der gleichen Zeichenkette "neben" verweist.
Von der Verwendung des Vergleichsoperators == zum lexikalischen Vergleich wird abgeraten. Ob die Laufzeitumgebung interne Referenzen oder „wahrhaftige“ String-Objekte anlegt, hängt von verschiedenen (nicht immer überschaubaren) Faktoren ab.
Aufgaben: Nennen Sie einige Methode der Klasse StringBuilder
StringBuilder littleBuilder = new StringBuilder();
StringBuilder bigBuilder = new StringBuilder(1000);
StringBuilder sammelBuilder = new StringBuilder("Ein Anfang");
sammelBuilder.append(", aber jetzt geht es weiter");
sammelBuilder.insert(30, " ein wenig");
sammelBuilder.delete(12, 17);
sammelBuilder.insert(3, "etwas");
StringBuilder vorname = new StringBuilder("Heinz");
StringBuilder vollerName = vorname.append("Müller");
Die Zeichenkette eines StringBuilder-Objekts kann ebenso wie diejenige eines String-Objekts mit der folgenden Anweisung ausgegeben werden: System.out.println(sammelBuilder);
Begriff: Welche Bedeutung hat der Begriff Taxonomie?
Begriff: Beschreiben Sie den Begriff der Vererbung.
Bei der Klassenvererbung (engl. class inheritance) erbt eine UnterklasseU die Attribute und Methoden einschließlich ihrer Implementierung von der OberklasseO, von der U abgeleitet wurde. Mittels Vererbung erhalten wir die Möglichkeit, auszudrücken, dass eine Klasse (hier U) ähnlich einer anderen Klasse (hier O) ist, mit gewissen Unterschieden, die sie spezieller machen. Um diese Unterschiede auszudrücken, können der abgeleiteten Klasse neue Attribute und Methoden hinzugefügt werden, oder die Implementierungen der geerbten Methoden verändert werden. Durch die Vererbung wird zwischen Unter- und Oberklasse eine ist-ein-Beziehung erzeugt
Beispiel: Vererbung einer Klasse in Java.
Wollen wir nun in Java ausdrücken, dass die Klasse Pflanze eine Unterklasse der extends Klasse Artikel ist, so geschieht dies mit Hilfe des Schlüsselworts extends und dem Namen der Oberklasse. [JLS: § 8.1.4]
class Artikel {
double preis;
double lieferePreis() {
return this.preis;
}
void legePreisFest(final double neuerPreis) {
this.preis = neuerPreis;
}
}
class Pflanze extends Artikel { }
Verständnis: Erläutern Sie die Beziehung der Unter- und Oberklassen. Subtyp und Typenverträglichkeit.
Jede Klasse definiert einen eigenen Objekttyp. Durch die Vererbung entstehen zwischen den Objekttypen Beziehungen. Ein durch eine Klasse U festgelegter Typ ist genau dann ein Subtyp einer durch die Klasse O festgelegten Typs, wenn U eine Unterklasse von O ist. Wir sagen auch, der Objekttyp U ist verträglich mit dem Objekttyp O. Der Typ des null-Verweises ist mit allen Objekttypen verträglich. [JLS: § 4.10.2]
Aufgabe: Wie kann verhindert werden, dass Syubtypen von Klassen gebildet werden.
Durch die folgenden Anweisungen, wird beispielhaft gezeigt, wie eine Klasse geschütz werden kann.
class Zubehoer extends Artikel { } final class DiverseDekoration extends Zubehoer { }
Begriff: Substitutionsprinzip
Vererbung bedeutet, dass jedes Objekt der Unterklasse auch eines der Oberklasse ist. Jede Pflanze ist somit auch ein Artikel. Ein Objekt der Unterklasse kann somit überall verwendet werden, wo ein Objekt der Oberklasse erwartet wird. Diesen Umstand nennen wir Substitutionsprinzip. Objekte der Unterklasse substituieren Substitutionsprinzip Objekte der Oberklasse. Dies ist immer möglich, da die Unterklasse alle Attribute und Methoden der Oberklasse erbt.
Bemerkung: Wir können Verweisvariablen vom Typ der Oberklasse ein Objekt der Unterklasse zuweisen.
Artikel a = new Pflanze();
Dabei muss beachtet werden, dass an a dennoch nur auf Attribute und Methoden der Klasse Artikel zugegriffen werden kann.
a.legePreisFest(15.0);
// ist möglich
a.legeLagertemperaturFest(10.0);
// ist nicht möglich, da die
// Klasse Artikel diese
// Methode nicht besitzt
Aufgabe: Wofür wird die Methode instanceof verwendet?
Manchmal ist es nötig zu prüfen, ob eine Variable auf ein Objekt einer Unterklasse verweist. Dies kann mit Hilfe des Operators instanceof geschehen. Der Ausdruck x instanceof Y liefert genau dann true, wenn x nicht der nullVerweis ist und wenn die Klasse des Objekts, auf das x verweist ein Subtyp von Y ist. [JLS: § 15.20.2]
Artikel a = new Artikel();
Artikel p = new Pflanze();
boolean aIstPflanze = a instanceof Pflanze; // liefert
false
boolean pIstPflanze = p instanceof Plfanze; // liefert true
Beispiel: explizite Typumwandlung
// erzeugt einen Übersetzungsfehler:
double temp1 = p.liefereLagertemperatur();
// liefert das gewünschte Ergebnis:
double temp2 = ((Pflanze) p).liefereLagertemperatur();
Die explizite Typumwandlung kann auch in Verbindung mit einer Zuweisung verwendet werden. Artikel k = new Pflanze(); Pflanze m = (Pflanze) k;
Sowohl bei einer expliziten Typumwandlung, als auch bei einem instanceof-Ausdruck kann ein Übersetzungsfehler auftreten, wenn es zwischen den Typen auf Grund der Typhierarchie auf keinen Fall eine Subtypbeziehung geben kann.
Beispiel: Übersetzungsfehler
class A { }
class B extends A { }
class C extends A { }
B b = new B();
boolean isInstance = b instanceof C; // liefert Übersetzungsfehler
C c = (C) b; // liefert Übersetzungsfehler
Die beiden Anweisungen liefern Übersetzungsfehler, da ein Exemplar vom Typ B auf Grund der Klassenhierarchie niemals ein Exemplar vom Typ C sein kann.
Aufgabe: Beschreiben Sie die implizite Typenumwandlung
Pflanze s = new Pflanze();
Artikel t;
t = s;
Der statische Typ einer Verweisvariablen ist immer der Typ, der bei der Deklaration vereinbart wurde. Er kann sich nicht im Laufe eines Programms verändern.
Definition: Dynamischer Typ einer Verweisvariablen
Unter dem dynamischen Typ einer Verweisvariablen verstehen wir den Typ des Objekts, auf das die Variable zu diesem Zeitpunkt verweist. Da eine Verweisvariable im Laufe eines Programms auf verschiedene Objekte verweisen kann, kann sich auch der dynamische Typ ändern. Der dynamische Typ ist immer ein Subtyp des statischen Typs.
Wenn ein Cast auf einen Verweis angewendet wird, so wird lediglich der statische Typ verändert. Der dynamische Typ bleibt unverändert. So ist zum Beispiel der statische Typ des Ausdrucks ((Artikel) new Pflanze()) der Objekttyp Artikel, der dynamische Typ hingegen Pflanze.
-
- 1 / 94
-