Objektorientierte Programmierung OOP
KE4 Vererbung; Begriffe, Beispiele und Definitionen
KE4 Vererbung; Begriffe, Beispiele und Definitionen
Kartei Details
Karten | 94 |
---|---|
Sprache | Deutsch |
Kategorie | Informatik |
Stufe | Universität |
Erstellt / Aktualisiert | 28.02.2013 / 18.05.2022 |
Weblink |
https://card2brain.ch/box/objektorientierte_programmierung_oop6
|
Einbinden |
<iframe src="https://card2brain.ch/box/objektorientierte_programmierung_oop6/embed" width="780" height="150" scrolling="no" frameborder="0"></iframe>
|
Beispiel: Paket
Eine vollständige Anwendung „Blumenladenverwaltung“ könnte beispielsweise in folgende Pakete aufgeteilt sein:
• Ein Paket kunde, das die Klassen zur Verwaltung von Kunden einschließlich Rechnungen, Bestellungen usw. enthält.
• Ein Paket waren, das die Klassen zum Einkauf und Verkauf von Artikeln enthält.
• Ein Paket laden, das die übergeordnete Anwendungslogik für die verschiedenen Anwendungsfälle enthält.
• Ein Paket gui, das Komponenten der grafischen Benutzungsschnittstelle enthält.
Java-Konvention: Paket
Nach der Konvention enthalten Paketnamen keine Großbuchstaben.
Bedeutung: Gültigkeitsbereich in einer Klasse
Der Gültigkeitsbereich eines einfachen Klassennamens wie Rechnung erstreckt sich auf das Paket, in dem sich die Klasse Rechnung befindet. Gehört die Klasse Rechnung zu einem Paket kunde, so können alle anderen Klassen aus dem Paket kunde die Klasse Rechnung mit ihrem einfachen Namen ansprechen. Eine Klasse, die nicht zum Paket kunde gehört, muss hingegen einen vollständigen Namen (engl. qualified name), zusammengesetzt aus dem Paketnamen und dem Klassennamen, verwenden [JLS: § 6.7]:
kunde.Rechnung
Bedeutung: Importanweisung
Eine explizite Benennung des Paketnamens für eine externe Klasse ist normaler weise nicht erforderlich, wenn wir eine Klasse mit einer Importanweisung wie folgt einbinden [JLS: § 7.5]:
import kunde.Rechnung;
Die Importanweisung muss nach der Paketanweisung und vor der Klassendefinition platziert werden. Durch eine Importanweisung wird der Name der importierten Klasse sichtbar gemacht. Die importierte Definition muss nun nicht mehr mit vollständigem Namen angesprochen werden, der Klassenname genügt. Wenn mehr als eine Klasse aus einem Paket importiert werden muss, können wir Klassen nach Bedarf einbinden, indem wir folgende Importanweisung benutzen [JLS: § 7.5.2]:
import kunde.*;
Nun werden alle Namen des importierten Pakets (z. B. kunde) automatisch importiert.
Beispiel: Import von Paketen
Eine Klasse Bestellung im Paket kunde könnte folgende Importanweisungen enthalten:
package kunde;
import waren.Artikel;
import waren.ArtikelListe;
class Bestellung {
...
}
oder alle Klassen des Pakets waren gemeinsam
importieren: package kunde;
import waren.*;
class Bestellung {
...
}
Bemerkung: Unterpaket
Beachten Sie, dass sich die Importanweisung mit Platzhalter nicht automatisch auf Unterpakete erstreckt. Klassen in einem Unterpaket b eines Pakets a werden durch die Anweisung
import a.b.*;
importiert. Sollen sowohl Klassen aus dem Paket a, als auch Klassen aus dem Paket a.b importiert werden, so sind zwei Importanweisungen erforderlich:
import a.*;
import a.b.*;
Aufgabe: Was versteht man unter dem Geheimprinzip in Java?
In der Programmierung versucht man das Verhalten von der konkreten Implementierung zu trennen. Für die Verwendung eines Programms bzw. einer Klasse muss lediglich bekannt sein, was für eine Funktionalität geboten wird, aber nicht wie genau sie umgesetzt ist. Dieses Konzept nennen wir auch Geheimnisprinzip, da die genaue Implementierung für Außenstehende ein Geheimnis ist. Dieses Prinzip bringt eine Reihe von Vorteilen mit sich. Da die Interna der Klasse nach außen hin nicht bekannt sind, verursachen Änderungen an der Implementierung keine Probleme, solange die vereinbarte Funktionalität beibehalten wird.
Definition: Zugriffskontrolle bei eigenständigen Klassen
Eine Klasse ist öffentlich (engl. public) zugänglich, falls in der Klassendefinition dem Schlüsselwort class das Schlüsselwort public vorangestellt ist:
public class className {
...
}
Eine öffentliche Klasse ist in allen Paketen sichtbar. Falls public fehlt, so ist lediglich der Standardzugriff (engl. default access) von innerhalb des Pakets, zu dem die Klasse gehört, möglich. Andere Zugriffsmodifikatoren sind bei eigenständigen Klassen nicht zulässig. [JLS: § 6.6.1]
Beispiel: Zugriff auf Klassen
Gegeben seien die Pakete a, a.c und b mit den folgenden Klassendeklarationen.
package a;
class K {
// Hier sind die Klassen
// L, M und N sichtbar
}
public class L {
// Hier sind die Klassen
// K, L und N sichtbar
}
package a.c;
public class M {
// Hier sind die Klassen
// L und N sichtbar
}
package b;
public class N {
// Hier sind die Klassen
// L, M und P sichtbar
}
class P {
// Hier sind die Klassen
// L, M und N sichtbar
}
Die Klasse K ist nur im Paket a sichtbar. Das Paket a.c ist kein Bestandteil von a, somit ist K dort nicht sichtbar. Die Klasse P ist nur im Paket b sichtbar
Definition: Zugriffskontrolle bei Attributen
Bei der Attributdeklaration kann dem Typen noch ein Zugriffsmodifikator vorange stellt werden. [JLS: § 8.3.1]
Zugriffsmodifikator Typ Name;
Java-Konvention: Zugriffsmodifikation
Treten bei einer Attributdeklaration sowohl ein Zugriffsmodifikator als auch das Schlüsselwort final oder static auf, so wird zuerst der Zugriffsmodifkator und anschließend static und dann final genannt
Zugriffskontrolle bei Bei der Methodendeklaration kann dem Ergebnistyp noch ein Zugriffsmodifikator Methoden vorangestellt werden. [JLS: § 8.4.3] Zugriffsmodifikator Ergebnistyp Name(Parameterliste) { }
Definition: Zugriffskontrolle bei Konstruktoren
Bei der Konstruktordeklaration kann dem Namen noch ein Zugriffsmodifikator vor- Zugriffskontrolle bei angestellt werden. [JLS: § 8.8.3] Konstruktoren
Zugriffsmodifikator Name(Parameterliste) {
}
Definition: Öffentliche (public) Klassenelemente
Ein Klassenelement, dass als public deklariert ist, ist überall dort sichtbar, wo auch die Klasse sichtbar ist.
Bemerkung: main()-Methode
Mit unseren jetzigen Kenntnissen ist es uns auch möglich, alle Bestandteile der main()-Methode zu verstehen. Die main()-Methode ist eine öffentliche (public) Klassenmethode (static), die kein Ergebnis zurückgibt (void) und als einziges Argument ein Zeichenkettenfeld (String[] args) akzeptiert. Soll im Programm auf die übergebenen Argumente zugegriffen werden, so kann dies über das Zeichenkettenfeld geschehen. Werden zum Beispiel Zahlen übergeben, so müssen diese mit geeigneten Methoden der API in Werte primitiver Datentypen konvertiert werden.
Ein Klassenelement, dessen Deklaration keinen Zugriffsmodifikator enthält, ist innerhalb des Paketes sichtbar, in dem die zugehörige Klasse enthalten ist. [JLS: § 6.6.1]
Definition: Private (private) Klassenelemente
Ein Klassenelement, dass als private deklariert ist, ist nur innerhalb der Klasse sichtbar
Attribute werden in der Regel als private deklariert, damit sie so von außerhalb der Klasse nicht direkt verändert werden können. Auch Hilfsmethoden werden in der Regel als private deklariert.
Ein Klassenelement, dass als protected deklariert ist, ist innerhalb des Paketes der Klasse sichtbar. Eine Unterklasse U hat auch außerhalb des Pakets der Oberklasse O Zugriff auf dieses Element. Dazu muss jedoch O als öffentlich deklariert sein, und der Zugriff muss an einem Element vom Typ U oder einem Subtyp von U ausgeführt werden. [JLS: § 6.6.1, § 6.6.2]
Methoden und Attribute die nicht jedem Benutzer zur Verfügung stehen sollen, jedoch von Unterklassen benötigt werden, werden in der Regel als protected deklariert.
Darstellung: Zugriffmodifikatoren für Klassenelemente in UML
Die Zugriffmodifikatoren für Klassenelemente können auch in der UML dargestellt werden. Die UML bietet wie Java vier verschiedene Modifikatoren (VisibilityKind): public, private, protected und package. Dabei entspricht package dem Standardzugriff von Java. Die Bedeutung von protected unterscheidet sich jedoch, da in der UML dieser Modifikator andeutet, dass nur Unterklassen Zugriff auf dieses Element haben, Klassen im gleichen Paket jedoch nicht. Die Modifikatoren werden in der UML dem Methodennamen vorangestellt und durch die folgenden Zeichen dargestellt: siehe Abbildung
Hinweis: überschrieben von Methoden
Beim Überschreiben von Methoden darf der Zugriff in der Unterklasse höchstens erweitert, aber niemals eingeschränkt werden. d. h. der Zugriffsmodifikator der Methode in der Unterklasse muss größer und gleich dem der Oberklasse sein [JLS: § 8.4.8.3]. Dabei gilt die folgende Ordnung:
public > protected > Standardzugriff > private.
Aufgabe: Nennen Sie die Vererbungsregeln
Für die Vererbung gelten für Klassen folgende Regeln:
• Eine Unterklasse erbt alle öffentlichen Attribute und Methoden der Oberklasse,
• alle geschützten Attribute und Methoden der Oberklasse sowie
• alle Attribute und Methoden ohne Zugriffsmodifikator der Oberklasse, wenn sich die Oberklasse im gleichen Paket befindet.
Konstruktoren werden nicht vererbt.
Eine finale Klasse kann nicht erweitert, also auch nicht beerbt werden.
Da eine Oberklasse selbst Attribute und Methoden von ihrer Oberklasse geerbt haben kann, erhalten wir Vererbungsketten, die von der obersten Klasse bis zu den Blättern einer Klassenhierarchie reichen. Lokale Variablen, d. h. Variablen, die innerhalb von Methoden deklariert sind, sind nicht von außerhalb der jeweiligen Methode und somit auch niemals von außerhalb der Klasse zugänglich.
Eine abstrakte Klasse wird durch das vorangestellte Schlüsselwort abstract vereinbart. [JLS: § 8.1.1.1] Von abstrakten Klassen können keine Exemplare erzeugt werden. Eine abstrakte Klasse kann abstrakte Methoden besitzen.
Eine abstrakte Methode weist nur eine Signatur und einen Ergebnistyp oder void auf und wird durch das vorangestellte Schlüsselwort abstract vereinbart. [JLS: § 8.4.3.1] Die Vereinbarung einer abstrakten Methode wird mit einem Semikolon abgeschlossen: Zugriffsmodifikator abstract Ergebnistyp Name(Parameterliste);
Bemerkung: abstrakte Methoden und Klassen
Wenn in einer Klasse eine oder mehrere Methoden als abstract vereinbart sind, muss die Klasse auch als abstract vereinbart sein.
Eine Klasse kann jedoch auch als abstract erklärt werden, wenn keine ihrer Methoden als abstract deklariert ist.
Für abstrakte Klassen gelten folgende Vererbungsregeln:
• Jede konkrete Klasse, die von einer abstrakten Klasse erbt, muss die von der abstrakten Klasse geerbten abstrakten Methoden überschreiben und implementieren.
• Wenn eine oder mehrere abstrakte Methoden der abstrakten Oberklasse in der Unterklasse nicht implementiert werden, dann bleiben diese Methoden abstrakt, und die Unterklasse muss auch abstrakt sein.
Darstellung UML: Abstrakt
In der UML können abstrakte Klassen mit dem Stereotyp <<abstract>> gekennzeichnet werden.
Bemerkung: Verwendung abstrakter Methoden und Klassen
Abstrakte Klassen sind nützlich, wenn wir für manche Methoden Implementierungen angeben wollen und für andere nicht. Die Implementierung einer abstrakten Methode in verschiedenen Unterklassen muss mit der Vereinbarung der abstrakten Methode übereinstimmen. Die Vereinbarung abstrakter Methode empfiehlt sich, wenn für eine Methode keine sinnvolle Standardimplementierung in einer Oberklasse angegeben werden kann.
Bisweilen sind abstrakte Klassen nützlich, um zu gewährleisten, dass nur Objekte von Unterklassen erzeugt werden.
Beispiel: Pflanzgefäße und Blumenerde
Als Service für Käufer von Blumentöpfen und anderen Pflanzgefäßen soll das Volumen der insgesamt benötigten Blumenerde berechnet werden. Wir verwenden eine kleine Klassenhierarchie mit verschiedenen Arten von Pflanzgefäßen als Subklassen einer abstrakten Oberklasse Pflanzgefaess. Die Abbildung zeigt die Subklassen Topf für konische Blumentöpfe und Kiste für quaderförmige Blumenkisten.
In Kurzeinheit 4 S.279 finden Sie das komplette Beispiel.
Da jeder Artikel in einem Blumenladen einer möglichst spezifischen Warengruppe angehören sollte, ist es durchaus sinnvoll, die Erzeugung von Objekten allgemeiner Oberklassen zu verhindern. So könnten etwa die Klasse Artikel oder auch andere Oberklassen wie Pflanze als abstract deklariert werden.
Bedeutung: Konstruktoren in abstrakten Klassen
Ein Konstruktor kann nicht als abstract deklariert werden. Da Konstruktoren nicht überschrieben werden können, gibt es keine Möglichkeit, einen Konstruktor erst in einer abgeleiteten Klasse zu implementieren. Eine abstrakte Klasse darf aber durchaus Konstruktoren implementieren, die dann von Unterklassen mit Hilfe von super() verwendet werden können.
Hinweis: abstrakte Klassen und Methoden in Verbindung mit private und static
Als private deklarierte Methoden können nicht als abstract vereinbart werden. Der Grund für diese Einschränkung liegt auf der Hand: sie könnten in einer abgeleiteten Klasse nicht überschrieben werden, weil private Methoden und Attribute außerhalb der definierenden Klasse nicht sichtbar sind. Statische Methoden können ebenfalls nicht als abstract vereinbart werden. Java lässt es auch nicht zu, eine Klasse als final und abstract zu deklarieren. Wenn eine Klasse als final vereinbart wird, drücken wir damit aus, dass von dieser Klasse keine Unterklassen abgeleitet werden können. Damit besteht auch keine Möglichkeit, abstrakte Methoden in dieser Klasse zu vereinbaren. Analog können auch Methoden nicht als final und abstract deklariert werden.
Java-Konvention: public und abstract
Treten bei einer Klasse oder bei einer Methode die Modifikatoren public und abstract gemeinsam auf, so wird zuerst public genannt.
Definition: Schnittstellenvereinbarung
Eine Schnittstelle wird wie folgt vereinbart [JLS: § 9.1]:
interface Schnittstellenname {
Attributdeklarationen
Methodendeklarationen
}
Attributvereinbarungen können nur öffentliche finale Klassenattribute enthalten. Die Schlüsselwörter public static final sind zur Deklaration nicht erforderlich. [JLS: § 9.3] Alle Methodendeklarationen sind öffentlich, nicht-statisch und abstrakt. Die Schlüsselwörter public abstract sind zur Deklaration nicht erforderlich. [JLS: § 9.4] Der Rumpf der Schnittstelle darf leer sein. Eine Schnittstelle kann mit Hilfe des Schlüsselworts extends mehrere andere Schnittstellen erweitern.
Aufgabe: Wie lauten die Verbungsregeln von Schnittstellen?
Für Schnittstellen gelten folgende Vererbungsregeln:
• Schnittstellen können von anderen Schnittstellen erweitert (engl. extend) werden. Sie können jedoch nicht durch Klassen erweitert werden.
• Klassen können Schnittstellen lediglich implementieren.
• Eine Klasse kann mehrere Schnittstellen implementieren.
• Schnittstellenmethoden müssen in einer Klassenimplementierung als public vereinbart werden, da alle Schnittstellenmethoden implizit public sind.
• Wenn Klasse A Schnittstelle B implementiert, ist A ein Subtyp von B. Somit kann ein Objekt vom Typ A überall dort verwendet werden, wo ein Objekt vom Typ B erwartet wird.
• Eine Klasse kann zugleich eine (oder mehrere) Schnittstelle(n) implementieren und eine Klasse erweitern.
Definition: Implementierung von Schnittstellen durch Klassen
Java benutzt das Schlüsselwort implements, um anzuzeigen, dass eine Klasse eine oder mehrere Schnittstellen implementiert [JLS: § 8.1.5]:
class Klassenname implements I [, I2, ..., In ] {
...
}
Eine Klasse muss jede Methode der Schnittstellen I, I2, ..., In mit den vereinbarten Ergebnistypen und Parametern implementieren oder, falls sie nicht alle Schnittstellenmethoden implementiert, als abstract gekennzeichnet sein.
Darstellung UML: Interface
In UML-Klassendiagrammen werden Schnittstellen durch das Stereotyp <<interface>> gekennzeichnet. Eine implements-Beziehung wird durch einen Pfeil mit gestrichelter Linie und unausgefüllter Pfeilspitze ausgedrückt.
Bemerkung: Mehrfachvererbung
Bei Schnittstellen wird in Java das Prinzip der Einfachvererbung durchbrochen und eine Art von Mehrfachvererbung (engl. multiple inheritance) zugelassen. Mehrfachvererbung bedeutet, dass ein Typ mehrere Obertypen hat.
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.