Vererbung ist ein zentrales OOP-Konzept, das die Wiederverwendung und Erweiterung von Klassen ermöglicht. In ABAP wird Vererbung mit INHERITING FROM realisiert. ABSTRACT und FINAL steuern die Erweiterbarkeit.
Grundkonzepte
- Vererbung: Eine Klasse (Unterklasse) übernimmt Eigenschaften einer anderen (Basisklasse)
- ABSTRACT: Klassen/Methoden, die nicht direkt instanziiert/aufgerufen werden können
- FINAL: Klassen/Methoden, die nicht weiter vererbt/überschrieben werden können
- REDEFINITION: Methoden der Basisklasse in Unterklasse überschreiben
Syntax
Vererbung
CLASS <unterklasse> DEFINITION INHERITING FROM <basisklasse>.Abstract
CLASS <klassenname> DEFINITION ABSTRACT. METHODS: <methodenname> ABSTRACT.ENDCLASS.Final
CLASS <klassenname> DEFINITION FINAL. METHODS: <methodenname> FINAL.ENDCLASS.Redefinition
CLASS <unterklasse> DEFINITION INHERITING FROM <basisklasse>. METHODS: <methodenname> REDEFINITION.ENDCLASS.Beispiele
1. Einfache Vererbung
" BasisklasseCLASS lcl_vehicle DEFINITION. PUBLIC SECTION. METHODS: constructor IMPORTING iv_brand TYPE string, get_brand RETURNING VALUE(rv_brand) TYPE string, start.
PROTECTED SECTION. DATA: mv_brand TYPE string.ENDCLASS.
CLASS lcl_vehicle IMPLEMENTATION. METHOD constructor. mv_brand = iv_brand. ENDMETHOD.
METHOD get_brand. rv_brand = mv_brand. ENDMETHOD.
METHOD start. WRITE: / 'Fahrzeug startet...'. ENDMETHOD.ENDCLASS.
" UnterklasseCLASS lcl_car DEFINITION INHERITING FROM lcl_vehicle. PUBLIC SECTION. METHODS: constructor IMPORTING iv_brand TYPE string iv_doors TYPE i, get_doors RETURNING VALUE(rv_doors) TYPE i.
PRIVATE SECTION. DATA: mv_doors TYPE i.ENDCLASS.
CLASS lcl_car IMPLEMENTATION. METHOD constructor. " Basisklassen-Konstruktor aufrufen super->constructor( iv_brand ). mv_doors = iv_doors. ENDMETHOD.
METHOD get_doors. rv_doors = mv_doors. ENDMETHOD.ENDCLASS.
" VerwendungDATA: lo_car TYPE REF TO lcl_car.
lo_car = NEW #( iv_brand = 'BMW' iv_doors = 4 ).
WRITE: / lo_car->get_brand( ). " BMW (geerbt)WRITE: / lo_car->get_doors( ). " 4 (eigene Methode)lo_car->start( ). " Fahrzeug startet... (geerbt)2. Methoden überschreiben (REDEFINITION)
CLASS lcl_car DEFINITION INHERITING FROM lcl_vehicle. PUBLIC SECTION. METHODS: constructor IMPORTING iv_brand TYPE string iv_doors TYPE i, start REDEFINITION. " Methode überschreiben
PRIVATE SECTION. DATA: mv_doors TYPE i.ENDCLASS.
CLASS lcl_car IMPLEMENTATION. METHOD constructor. super->constructor( iv_brand ). mv_doors = iv_doors. ENDMETHOD.
METHOD start. " Eigene Implementierung WRITE: / 'Auto startet den Motor...'.
" Optional: Basisklassen-Methode aufrufen " super->start( ). ENDMETHOD.ENDCLASS.
" VerwendungDATA: lo_vehicle TYPE REF TO lcl_vehicle, lo_car TYPE REF TO lcl_car.
lo_vehicle = NEW lcl_vehicle( 'Generic' ).lo_vehicle->start( ). " Fahrzeug startet...
lo_car = NEW lcl_car( iv_brand = 'BMW' iv_doors = 4 ).lo_car->start( ). " Auto startet den Motor...3. SUPER – Basisklasse aufrufen
CLASS lcl_electric_car DEFINITION INHERITING FROM lcl_car. PUBLIC SECTION. METHODS: constructor IMPORTING iv_brand TYPE string iv_doors TYPE i iv_battery TYPE i, start REDEFINITION.
PRIVATE SECTION. DATA: mv_battery_capacity TYPE i.ENDCLASS.
CLASS lcl_electric_car IMPLEMENTATION. METHOD constructor. " Elternklassen-Konstruktor aufrufen super->constructor( iv_brand = iv_brand iv_doors = iv_doors ). mv_battery_capacity = iv_battery. ENDMETHOD.
METHOD start. WRITE: / 'Batterie-Check...'. WRITE: / |Kapazität: { mv_battery_capacity } kWh|.
" Methode der Basisklasse aufrufen super->start( ). ENDMETHOD.ENDCLASS.
" VerwendungDATA(lo_tesla) = NEW lcl_electric_car( iv_brand = 'Tesla' iv_doors = 4 iv_battery = 100).
lo_tesla->start( )." Ausgabe:" Batterie-Check..." Kapazität: 100 kWh" Auto startet den Motor...4. Abstrakte Klassen
" Abstrakte Klasse - kann nicht instanziiert werdenCLASS lcl_shape DEFINITION ABSTRACT. PUBLIC SECTION. " Abstrakte Methode - MUSS in Unterklasse implementiert werden METHODS: calculate_area ABSTRACT RETURNING VALUE(rv_area) TYPE f.
" Konkrete Methode - kann geerbt werden METHODS: get_name RETURNING VALUE(rv_name) TYPE string.
PROTECTED SECTION. DATA: mv_name TYPE string.ENDCLASS.
CLASS lcl_shape IMPLEMENTATION. METHOD get_name. rv_name = mv_name. ENDMETHOD. " calculate_area hat keine Implementierung (abstrakt)ENDCLASS.
" Konkrete Unterklasse: KreisCLASS lcl_circle DEFINITION INHERITING FROM lcl_shape. PUBLIC SECTION. METHODS: constructor IMPORTING iv_radius TYPE f, calculate_area REDEFINITION.
PRIVATE SECTION. DATA: mv_radius TYPE f. CONSTANTS: c_pi TYPE f VALUE '3.14159265'.ENDCLASS.
CLASS lcl_circle IMPLEMENTATION. METHOD constructor. super->constructor( ). mv_name = 'Kreis'. mv_radius = iv_radius. ENDMETHOD.
METHOD calculate_area. rv_area = c_pi * mv_radius ** 2. ENDMETHOD.ENDCLASS.
" Konkrete Unterklasse: RechteckCLASS lcl_rectangle DEFINITION INHERITING FROM lcl_shape. PUBLIC SECTION. METHODS: constructor IMPORTING iv_width TYPE f iv_height TYPE f, calculate_area REDEFINITION.
PRIVATE SECTION. DATA: mv_width TYPE f, mv_height TYPE f.ENDCLASS.
CLASS lcl_rectangle IMPLEMENTATION. METHOD constructor. super->constructor( ). mv_name = 'Rechteck'. mv_width = iv_width. mv_height = iv_height. ENDMETHOD.
METHOD calculate_area. rv_area = mv_width * mv_height. ENDMETHOD.ENDCLASS.
" VerwendungDATA: lt_shapes TYPE TABLE OF REF TO lcl_shape.
" lo_shape = NEW lcl_shape( ). " FEHLER! Abstrakt!
APPEND NEW lcl_circle( 5 ) TO lt_shapes.APPEND NEW lcl_rectangle( iv_width = 4 iv_height = 3 ) TO lt_shapes.
LOOP AT lt_shapes INTO DATA(lo_shape). WRITE: / lo_shape->get_name( ), ': Fläche =', lo_shape->calculate_area( ).ENDLOOP.
" Ausgabe:" Kreis: Fläche = 78.54" Rechteck: Fläche = 12.005. FINAL – Vererbung verhindern
" Finale Klasse - kann NICHT vererbt werdenCLASS lcl_singleton DEFINITION FINAL. PUBLIC SECTION. CLASS-METHODS: get_instance RETURNING VALUE(ro_instance) TYPE REF TO lcl_singleton.
PRIVATE SECTION. CLASS-DATA: go_instance TYPE REF TO lcl_singleton. METHODS: constructor.ENDCLASS.
CLASS lcl_singleton IMPLEMENTATION. METHOD constructor. " Privater Konstruktor ENDMETHOD.
METHOD get_instance. IF go_instance IS NOT BOUND. go_instance = NEW #( ). ENDIF. ro_instance = go_instance. ENDMETHOD.ENDCLASS.
" Diese Zeile würde Syntaxfehler verursachen:" CLASS lcl_child DEFINITION INHERITING FROM lcl_singleton." ..." ENDCLASS.6. FINAL Methoden
CLASS lcl_base DEFINITION. PUBLIC SECTION. " Diese Methode kann NICHT überschrieben werden METHODS: critical_operation FINAL.
" Diese Methode kann überschrieben werden METHODS: normal_operation.ENDCLASS.
CLASS lcl_base IMPLEMENTATION. METHOD critical_operation. WRITE: / 'Kritische Operation - nicht änderbar'. ENDMETHOD.
METHOD normal_operation. WRITE: / 'Normale Operation'. ENDMETHOD.ENDCLASS.
CLASS lcl_derived DEFINITION INHERITING FROM lcl_base. PUBLIC SECTION. " OK: normal_operation kann redefiniert werden METHODS: normal_operation REDEFINITION.
" FEHLER: critical_operation ist FINAL " METHODS: critical_operation REDEFINITION.ENDCLASS.
CLASS lcl_derived IMPLEMENTATION. METHOD normal_operation. WRITE: / 'Überschriebene Operation'. ENDMETHOD.ENDCLASS.7. PROTECTED – Zugriff für Unterklassen
CLASS lcl_account DEFINITION. PUBLIC SECTION. METHODS: constructor IMPORTING iv_balance TYPE p, get_balance RETURNING VALUE(rv_balance) TYPE p, deposit IMPORTING iv_amount TYPE p.
PROTECTED SECTION. " Nur für diese Klasse und Unterklassen sichtbar DATA: mv_balance TYPE p DECIMALS 2. METHODS: validate_amount IMPORTING iv_amount TYPE p RETURNING VALUE(rv_valid) TYPE abap_bool.
PRIVATE SECTION. " Nur für diese Klasse sichtbar DATA: mv_account_number TYPE string.ENDCLASS.
CLASS lcl_account IMPLEMENTATION. METHOD constructor. mv_balance = iv_balance. ENDMETHOD.
METHOD get_balance. rv_balance = mv_balance. ENDMETHOD.
METHOD deposit. IF validate_amount( iv_amount ). mv_balance = mv_balance + iv_amount. ENDIF. ENDMETHOD.
METHOD validate_amount. rv_valid = xsdbool( iv_amount > 0 ). ENDMETHOD.ENDCLASS.
CLASS lcl_savings_account DEFINITION INHERITING FROM lcl_account. PUBLIC SECTION. METHODS: add_interest IMPORTING iv_rate TYPE p.ENDCLASS.
CLASS lcl_savings_account IMPLEMENTATION. METHOD add_interest. " Zugriff auf PROTECTED mv_balance möglich DATA(lv_interest) = mv_balance * iv_rate / 100.
" Zugriff auf PROTECTED validate_amount möglich IF validate_amount( lv_interest ). mv_balance = mv_balance + lv_interest. ENDIF.
" FEHLER: mv_account_number ist PRIVATE " mv_account_number = '123'. ENDMETHOD.ENDCLASS.8. Polymorphismus
" Verschiedene FahrzeugtypenCLASS lcl_motorcycle DEFINITION INHERITING FROM lcl_vehicle. PUBLIC SECTION. METHODS: start REDEFINITION.ENDCLASS.
CLASS lcl_motorcycle IMPLEMENTATION. METHOD start. WRITE: / 'Motorrad brummt los...'. ENDMETHOD.ENDCLASS.
CLASS lcl_truck DEFINITION INHERITING FROM lcl_vehicle. PUBLIC SECTION. METHODS: start REDEFINITION.ENDCLASS.
CLASS lcl_truck IMPLEMENTATION. METHOD start. WRITE: / 'LKW-Diesel startet...'. ENDMETHOD.ENDCLASS.
" Polymorphe VerwendungDATA: lt_fleet TYPE TABLE OF REF TO lcl_vehicle.
APPEND NEW lcl_car( iv_brand = 'BMW' iv_doors = 4 ) TO lt_fleet.APPEND NEW lcl_motorcycle( 'Honda' ) TO lt_fleet.APPEND NEW lcl_truck( 'MAN' ) TO lt_fleet.
" Alle Fahrzeuge starten - jedes auf seine WeiseLOOP AT lt_fleet INTO DATA(lo_vehicle). lo_vehicle->start( ).ENDLOOP.
" Ausgabe:" Auto startet den Motor..." Motorrad brummt los..." LKW-Diesel startet...9. Typprüfung mit IS INSTANCE OF
DATA: lo_vehicle TYPE REF TO lcl_vehicle.
lo_vehicle = NEW lcl_car( iv_brand = 'Audi' iv_doors = 4 ).
" TypprüfungIF lo_vehicle IS INSTANCE OF lcl_car. WRITE: / 'Ist ein Auto'.
" Downcast für spezifische Methoden DATA(lo_car) = CAST lcl_car( lo_vehicle ). WRITE: / 'Türen:', lo_car->get_doors( ).ENDIF.
" Mit CASE TYPE OFCASE TYPE OF lo_vehicle. WHEN TYPE lcl_car. WRITE: / 'Auto erkannt'. WHEN TYPE lcl_motorcycle. WRITE: / 'Motorrad erkannt'. WHEN TYPE lcl_truck. WRITE: / 'LKW erkannt'. WHEN OTHERS. WRITE: / 'Unbekannter Fahrzeugtyp'.ENDCASE.10. Abstrakte Klasse mit Template Method Pattern
CLASS lcl_report DEFINITION ABSTRACT. PUBLIC SECTION. " Template Method - definiert den Ablauf METHODS: execute FINAL.
PROTECTED SECTION. " Hook Methods - von Unterklassen zu implementieren METHODS: fetch_data ABSTRACT, process_data ABSTRACT, display_output ABSTRACT.ENDCLASS.
CLASS lcl_report IMPLEMENTATION. METHOD execute. " Fester Ablauf, nicht überschreibbar (FINAL) WRITE: / 'Report startet...'. fetch_data( ). process_data( ). display_output( ). WRITE: / 'Report beendet.'. ENDMETHOD.ENDCLASS.
CLASS lcl_sales_report DEFINITION INHERITING FROM lcl_report. PROTECTED SECTION. METHODS: fetch_data REDEFINITION, process_data REDEFINITION, display_output REDEFINITION.ENDCLASS.
CLASS lcl_sales_report IMPLEMENTATION. METHOD fetch_data. WRITE: / ' Lade Verkaufsdaten...'. ENDMETHOD.
METHOD process_data. WRITE: / ' Berechne Umsätze...'. ENDMETHOD.
METHOD display_output. WRITE: / ' Zeige Verkaufsreport an.'. ENDMETHOD.ENDCLASS.
" VerwendungDATA(lo_report) = NEW lcl_sales_report( ).lo_report->execute( ).
" Ausgabe:" Report startet..." Lade Verkaufsdaten..." Berechne Umsätze..." Zeige Verkaufsreport an." Report beendet.Vererbungshierarchie
lcl_vehicle (Basisklasse) │ ├── lcl_car │ │ │ └── lcl_electric_car │ ├── lcl_motorcycle │ └── lcl_truckZusammenfassung
| Konzept | Schlüsselwort | Beschreibung |
|---|---|---|
| Vererbung | INHERITING FROM | Klasse erweitert andere Klasse |
| Überschreiben | REDEFINITION | Methode der Basisklasse neu implementieren |
| Basisklasse aufrufen | super-> | Zugriff auf Elternklasse |
| Abstrakt | ABSTRACT | Nicht instanziierbar / muss implementiert werden |
| Final | FINAL | Nicht vererbbar / nicht überschreibbar |
| Geschützt | PROTECTED | Sichtbar für Klasse und Unterklassen |
Wichtige Hinweise / Best Practice
- Vererbung für “ist-ein”-Beziehungen (Auto ist ein Fahrzeug).
- Für “hat-ein”-Beziehungen besser Komposition verwenden.
PROTECTEDfür Attribute/Methoden, die Unterklassen brauchen.ABSTRACTfür Basisklassen, die nur als Vorlage dienen.FINALfür Klassen/Methoden, die nicht verändert werden sollen.- Polymorphismus ermöglicht einheitliche Behandlung verschiedener Typen.
super->zum Aufrufen der Basisklassen-Implementierung.- Prüfen Sie Typen mit
IS INSTANCE OFvor demCAST. - Bevorzugen Sie
INTERFACEfür lose Kopplung. - Tiefe Vererbungshierarchien vermeiden (max. 2-3 Ebenen).