Show TOC Anfang des Inhaltsbereichs

Vererbung  Dokument im Navigationsbaum lokalisieren

Das Konzept der Vererbung erlaubt es, Klassen bei ihrer Definition von bereits vorhandenen Klassen abzuleiten. Hierzu dient der Zusatz INHERITING FROM in der Anweisung:

CLASS subclass DEFINITION INHERITING FROM superclass.

Die neu zu definierende Klasse subclass übernimmt bzw. erbt dabei alle Komponenten der bereits vorhandenen Klasse superclass. Man nennt die neue Klasse Unterklasse und die bereits vorhandene Klasse Oberklasse.

Ohne weitere Deklarationen enthält eine Unterklasse genau die Komponenten der Oberklasse. Es sind aber nur die Komponenten aus dem öffentlichen und geschützten Sichtbarkeitsbereich der Oberklasse in der Unterklasse sichtbar. Die privaten Komponenten der Oberklasse sind zwar auch in der Unterklasse vorhanden, aber nicht sichtbar. In einer Unterklasse können gleichnamige private Komponenten wie in der Oberklasse deklariert werden. Jede Klasse arbeitet mit ihren privaten Komponenten. Eine geerbte Methode der Oberklasse verwendet die privaten Attribute der Oberklasse und nicht eventuelle gleichnamige Attribute der Unterklasse.

Falls die Oberklasse keinen privaten Sichtbarkeitsbereich hat, ist die Unterklasse ein genaues Abbild der Oberklasse. In der Unterklasse können aber weitere Komponenten hinzu definiert werden. Diese dienen der Spezialisierung der Unterklasse gegenüber der Oberklasse. Wenn eine Unterklasse wieder als Oberklasse für eine neue Klasse dient, kann eine weitere Stufe der Spezialisierung durchgeführt werden.

Jede Klasse kann mehrere direkte Unterklassen, aber nur eine direkte Oberklasse haben. ABAP Objects verwirklicht somit die so genannte Einfachvererbung. Wenn Unterklassen von Oberklassen erben, die selbst Unterklassen sind, bilden die beteiligten Klassen einen Vererbungsbaum, dessen Spezialisierung bei jeder hinzukommenden Hierarchiestufe zunimmt. Umgekehrt nimmt die Spezialisierung zum Wurzelknoten des Vererbungsbaums hin ab. Der Wurzelknoten aller Vererbungsbäume in ABAP Objects ist die vordefinierte leere Klasse OBJECT. Diese Klasse ist die allgemeinste mögliche Klasse, denn sie enthält weder Attribute noch Methoden. Sie muss bei der Definition einer neuen Klasse nicht explizit als Oberklasse angegeben werden, sondern ist implizit immer vorhanden. Im Vererbungsbaum bezeichnen wir zwei benachbarte Knoten als direkte Ober- und Unterklasse und alle vorhergehenden und nachfolgenden Knoten zusammenfassend als Ober- und Unterklassen. Die Deklaration der Komponenten einer Unterklasse sind über ist über sämtliche Oberklassen des Vererbungsbaums verteilt.

Redefinition von Methoden

Jede Unterklasse enthält die Komponenten aller Klassen, die im Vererbungsbaum zwischen ihr und dem Wurzelknoten liegen. Die Sichtbarkeit einer Komponente ist immer gleich und kann nicht geändert werden. Es besteht aber die Möglichkeit, die öffentlichen und geschützten Instanzmethoden aller vorangehenden Oberklassen mit dem Zusatz REDEFINITION der Anweisung METHODS zu redefinieren, um sie an die gewünschte Spezialisierung anzupassen. Dabei kann die Schnittstelle einer redefinierten Methode nicht verändert werden. Die Methode wird lediglich unter gleichem Namen neu implementiert.

Die Deklaration der Methode liegt weiterhin bei der Oberklasse und auch ihre vorhergehende Implementierung bleibt dort erhalten. Die Implementierung der Redefinition wird bei der Unterklasse zusätzlich erzeugt und verschattet die Implementierung der Oberklasse.

Jede Referenz, die auf ein Objekt der Unterklasse zeigt, verwendet die redefinierte Methode, auch wenn sie mit Bezug auf eine Oberklasse typisiert wurde. Dies gilt insbesondere auch für die Selbstreferenz me->. Wenn beispielsweise eine Oberklassenmethode m1 einen Aufruf CALL METHOD [me->]m2 enthält und m2 in einer Unterklasse redefiniert wird, führt der Aufruf von m1 in einer Instanz der Oberklasse zur Ausführung der ursprünglichen Methode m2 und der Aufruf von m1 in einer Instanz der Unterklasse zur Ausführung der redefinierten Methode m2.

Innerhalb einer redefinierten Methode kann mit der Pseudoreferenz super->auf die verschattete Methode zugegriffen werden, z.B. um deren Funktionalität zu übernehmen und zu ergänzen.

Abstrakte und finale Methoden und Klassen

Mit den Zusätzen ABSTRACT und FINAL der Anweisungen METHODS und CLASS können abstrakte und finale Methoden bzw. Klassen definiert werden.

Abstrakte Methoden werden in abstrakten Klassen deklariert und können nicht in der gleichen Klasse, sondern erst in einer Unterklasse des Vererbungsbaums implementiert werden. Abstrakte Klassen können deshalb nicht instanziiert werden.

Finale Methoden können in Unterklassen nicht mehr redefiniert werden. Finale Klassen können keine weiteren Unterklassen haben und schließen einen Vererbungsbaum endgültig ab.

Referenzen auf Unterklassen und Polymorphie

Da eine Unterklasse sämtliche Komponenten aller Oberklassen entlang des Vererbungsbaums enthält und die Schnittstellen von Methoden nicht änderbar sind, kann eine Referenzvariable, die mit Bezug auf eine Oberklasse oder auf ein Interface, das von einer Oberklasse implementiert wird, typisiert ist, Referenzen auf Objekte sämtlicher Unterklassen dieser Oberklasse enthalten. D.h. der Inhalt einer Referenzvariablen, die mit Bezug auf eine Unterklasse typisiert ist, kann immer Referenzvariablen, die mit Bezug auf eine ihrer Oberklassen oder deren Interfaces typisiert ist, zugewiesen werden. Insbesondere kann die Zielvariable immer mit Bezug auf die Klasse OBJECT typisiert sein.

Weiterhin kann bei der Erzeugung eines Objekts mit CREATE OBJECT und einer Referenzvariablen, die mit Bezug auf eine Oberklasse typisiert ist, mit dem TYPE-Zusatz direkt eine Instanz einer Unterklasse erzeugt werden, auf die dann die Referenz in der Referenzvariablen zeigt.

Ein statischer Verwender kann mit einer Referenzvariablen genau die ihm sichtbaren Komponenten ansprechen, die schon in der Oberklasse enthalten sind, auf die sich die Referenzvariable bezieht. Er kann also keine in den Unterklassen hinzugekommenen Spezialisierungen ansprechen. Beim dynamischen Methodenaufruf sind dagegen alle Komponenten ansprechbar.

Wenn eine Instanzmethode in einer oder mehreren Unterklassen redefiniert wird, können nach einem Methodenaufruf über ein dieselbe Referenzvariable unterschiedliche Implementierungen der Methode ausgeführt werden, je nachdem an welcher Stelle des Vererbungsbaums die Klasse des referenzierten Objekts steht. Diese Eigenschaft, dass unterschiedliche Klassen die gleiche Schnittstelle haben und daher über Referenzvariablen eines einzigen Typs ansprechbar sind, wird Polymorphie genannt.

Namensraum von Komponenten

Eine Unterklasse enthält sämtliche Komponenten aller Oberklassen eines Vererbungsbaums. Davon sind die öffentlichen und geschützten Komponenten sichtbar. Aus diesem Grund liegen alle öffentlichen und geschützten Komponenten eines Vererbungsbaums in einem Namensraum und müssen eindeutige Namen haben. Private Komponenten müssen dagegen nur innerhalb einer Klasse eindeutige Namen haben.

Bei der Redefinition von Methoden verschattet die neuimplementierte Methode die gleichnamige Methode der Oberklasse. Sie ersetzt ab der Redefinition die vorhergehende Methode, so dass die Eindeutigkeit des Namens bestehen bleibt. Die Pseudoreferenz super-> kann in Unterklassen verwendet werden, um auf eine durch Redefinition verschattete Methode der direkten Oberklasse zuzugreifen.

Vererbung und statische Attribute

Statische Attribute sind wie alle Komponenten auch nur einmal pro Vererbungsbaum vorhanden. Eine Unterklasse hat Zugriff auf den Inhalt der öffentlichen und geschützten statischen Attribute aller Oberklassen. Umgekehrt teilt sich eine Oberklasse ihre öffentlichen und geschützten statischen Attribute mit allen Unterklassen. Statische Attribute sind in der Vererbung also nicht einer einzigen Klasse, sondern einem Teil des Vererbungsbaums zugeordnet. Änderungen können von außen über den Klassenkomponenten-Selektor mit allen beteiligten Klassennamen oder von innen in allen beteiligten Klassen ausgeführt werden und sind in allen beteiligten Klassen sichtbar.

Beim Ansprechen eines statischen Attributs, das zu einem Teil eines Vererbungsbaums gehört, wird unabhängig vom im Klassenkomponenten-Selektor verwendeten Klassennamen aber immer die Klasse angesprochen, in der das Attribut deklariert ist. Dies ist wichtig beim Aufruf der statischen Konstruktoren von Klassen in der Vererbung. Ein statischer Konstruktor wird bei der ersten Ansprache einer Klasse ausgeführt. Wenn ein statisches Attribut über den Klassennamen einer Unterklasse adressiert wird, aber in einer Oberklasse deklariert ist, wird nur der statische Konstruktor der Oberklasse ausgeführt.

Vererbung und Konstruktoren

Für Konstruktoren gelten in der Vererbung spezielle Regeln.

Instanz-Konstruktoren

Jede Klasse hat einen Instanz-Konstruktor namens constructor. Dies ist eine Abweichung von der Regel der eindeutigen Komponentennamen entlang eines Vererbungsbaums. Konsequenterweise sind die Instanz-Konstruktoren der einzelnen Klassen eines Vererbungsbaums völlig unabhängig voneinander. Instanz-Konstruktoren von Oberklassen können in Unterklassen nicht redefiniert werden und Instanz-Konstruktoren können ohnehin nicht explizit mit CALL METHOD CONSTRUCTOR aufgerufen werden. Dadurch kann es zu keinen Namensraumkonflikten kommen.

Der Instanz-Konstruktor einer Klasse wird beim Anlegen eines Objekts mit dem Befehl CREATE OBJECT ausgeführt. Da bei der Vererbung eine Unterklasse alle ihr sichtbaren Attribute ihrer Oberklassen enthält, deren Inhalt wiederum von Instanz-Konstruktoren ihrer Klassen gesetzt werden kann, muss der Instanz-Konstruktor einer Unterklasse dafür sorgen, dass auch die Instanz-Konstruktoren sämtlicher Oberklassen ausgeführt werden. Hierfür muss der Instanz-Konstruktor jeder Unterklasse einen Aufruf CALL METHOD SUPER->CONSTRUCTOR des Instanz-Konstruktors der direkten Oberklasse enthalten. Ausnahmen hiervon sind nur die direkten Unterklassen des Wurzelknotens OBJECT.

In Oberklassen, die keinen explizit definierten Instanz-Konstruktor haben, wird der implizit immer vorhandene Instanz-Konstruktor ausgeführt. Dieser sorgt automatisch für den Aufruf des Instanz-Konstruktors der nächsthöheren Oberklasse.

Beim Aufruf von Instanz-Konstruktoren müssen deren nichtoptionalen Schnittstellenparameter versorgt werden. Hier gibt es verschiedene Möglichkeiten:

·        Versorgung bei CREATE OBJECT

Wenn die Klasse des erzeugten Objekts einen Instanz-Konstruktor mit Schnittstelle hat, muss diese mit EXPORTING versorgt werden.

Wenn die Klasse des erzeugten Objekts einen Instanz-Konstruktor ohne Schnittstelle hat, werden keine Parameter übergeben.

Wenn die Klasse des erzeugten Objekts keinen expliziten Instanz-Konstruktor hat, muss im Vererbungsbaum bis zur nächsten Oberklasse gegangen werden, die einen expliziten Instanz-Konstruktor hat. Hat dieser eine Schnittstelle, muss diese mit EXPORTING versorgt werden, ansonsten werden keine Parameter übergeben.

·        Versorgung bei CALL METHOD SUPER->CONSTRUCTOR

Wenn die direkte Oberklasse einen Instanz-Konstruktor mit Schnittstelle hat, muss diese mit EXPORTING versorgt werden.

Wenn die direkte Oberklasse einen Instanz-Konstruktor ohne Schnittstelle hat, werden keine Parameter übergeben.

Wenn die direkte Oberklasse keinen expliziten Instanz-Konstruktor hat, muss im Vererbungsbaum bis zur nächsten Oberklasse gegangen werden, die einen expliziten Instanz-Konstruktor hat. Hat dieser eine Schnittstelle, muss diese mit EXPORTING versorgt werden, ansonsten werden keine Parameter übergeben.

Sowohl bei CREATE OBJECT wie auch bei CALL METHOD SUPER->CONSTRUCTOR muss also der nächstmögliche explizite Instanz-Konstruktor betrachtet werden und, falls vorhanden, dessen Schnittstelle versorgt werden. Das Gleiche gilt für die Ausnahmebehandlung in Instanz-Konstruktoren. Das Arbeiten mit der Vererbung bedarf also einer genauen Kenntnis des gesamten Vererbungsbaums, da es durchaus passieren kann, dass beim Erzeugen eines Objekts einer Klasse, die im Vererbungsbaum weit unten liegt, Parameter an den Konstruktor einer viel näher zum Wurzelknoten hin liegenden Klasse übergeben werden müssen.

Der Instanz-Konstruktor einer Unterklasse wird durch den von der Syntax geforderten Aufruf CALL METHOD SUPER->CONSTRUCTOR in zwei Teile zerlegt. In den Anweisungen vor dem Aufruf verhält sich der Konstruktor wie eine statische Methode, d.h. er hat keinen Zugriff auf die Instanzattribute seiner Klasse. Erst nach dem Aufruf können auch Instanzattribute adressiert werden. Die Anweisungen vor dem Aufruf dienen der Bestimmung von Aktualparametern für die Schnittstelle des Instanz-Konstruktors einer Oberklasse. Hierfür können nur statische Attribute oder lokale Daten verwendet werden.

Bei der Instantiierung einer Unterklasse findet also ein geschachtelter Aufruf der Instanz-Konstruktoren von der Unterklasse zu den Oberklassen hin statt, wobei aber erst in der tiefsten Schachtelungsebene, d.h. in der höchsten Oberklasse, deren Instanzattribute adressiert werden können. Bei der Rückkehr in die Konstruktoren der darunterliegenden Unterklassen können dort auch deren Instanzattribute sukzessive adressiert werden.

In Konstruktoren sind die Methoden von Unterklassen nicht sichtbar. Falls ein Instanzkonstruktor eine Instanzmethode der gleichen Klasse über die implizite Selbstreferenz me-> aufruft, wird die Methode so aufgerufen, wie sie in der Klasse des Instanzkonstruktors implementiert ist und nicht die eventuell redefinierte Methode der zu instantiierenden Unterklasse. Dies ist eine Ausnahme von der Regel, dass beim Aufruf von Instanzmethoden immer die Implementierung in der Klasse aufgerufen wird, auf deren Instanz die Referenz gerade zeigt.

Statische Konstruktoren

Jede Klasse hat einen statischen Konstruktor namens class_constructor. Bezüglich des Namensraums entlang eines Vererbungsbaums gilt für den statischen Konstruktor das gleiche wie für den Instanz-Konstruktor.

Bei der ersten Ansprache einer Unterklasse in einem Programm wird der statische Konstruktor ausgeführt. Zuvor müssen aber die vorhergehenden statischen Konstruktoren des gesamten Vererbungsbaums ausgeführt worden sein. Andererseits kann ein statischer Konstruktor nur ein einziges Mal zur Laufzeit eines Programms aufgerufen werden. Deshalb wird bei erster Ansprache einer Unterklasse nach der nächst höheren Oberklasse gesucht, deren statischer Konstruktor noch nicht ausgeführt wurde. Dann wird erst deren statischer Konstruktor ausgeführt und danach sukzessive der aller folgenden Unterklassen bis zur angesprochenen Unterklasse.

 

Siehe auch:

Übersichtsgrafiken

Einführendes Beispiel zur Vererbung

Ende des Inhaltsbereichs