Die CLASS-Anweisung ist das Fundament der objektorientierten Programmierung (OOP) in ABAP. Mit Klassen können Sie Daten (Attribute) und Funktionen (Methoden) in wiederverwendbaren Einheiten zusammenfassen.
Grundkonzept
Eine Klasse ist eine Vorlage (Blueprint) für Objekte. Sie definiert:
- Attribute: Variablen, die den Zustand eines Objekts speichern
- Methoden: Funktionen, die das Verhalten eines Objekts definieren
Ein Objekt ist eine konkrete Instanz einer Klasse.
Syntax
Klassendefinition
CLASS <klassenname> DEFINITION. PUBLIC SECTION. " Öffentlich zugängliche Elemente DATA: <attribut> TYPE <typ>. METHODS: <methode> [IMPORTING/EXPORTING/RETURNING ...].
PROTECTED SECTION. " Nur für diese Klasse und Unterklassen zugänglich
PRIVATE SECTION. " Nur innerhalb dieser Klasse zugänglichENDCLASS.Klassenimplementierung
CLASS <klassenname> IMPLEMENTATION. METHOD <methode>. " Methodenlogik ENDMETHOD.ENDCLASS.Sichtbarkeitsbereiche
| Bereich | Zugriff |
|---|---|
PUBLIC | Von überall zugänglich |
PROTECTED | Nur Klasse selbst und Unterklassen |
PRIVATE | Nur innerhalb der Klasse |
Beispiele
1. Einfache Klasse definieren und verwenden
" DefinitionCLASS lcl_counter DEFINITION. PUBLIC SECTION. METHODS: increment, get_value RETURNING VALUE(rv_value) TYPE i. PRIVATE SECTION. DATA: mv_count TYPE i VALUE 0.ENDCLASS.
" ImplementierungCLASS lcl_counter IMPLEMENTATION. METHOD increment. mv_count = mv_count + 1. ENDMETHOD.
METHOD get_value. rv_value = mv_count. ENDMETHOD.ENDCLASS.
" VerwendungSTART-OF-SELECTION. DATA: lo_counter TYPE REF TO lcl_counter.
" Objekt erzeugen CREATE OBJECT lo_counter. " Oder modern: lo_counter = NEW #( ).
lo_counter->increment( ). lo_counter->increment( ).
WRITE: / 'Zählerstand:', lo_counter->get_value( ). " Ausgabe: Zählerstand: 22. Klasse mit Konstruktor
Der Konstruktor ist eine spezielle Methode, die beim Erzeugen eines Objekts automatisch aufgerufen wird:
CLASS lcl_person DEFINITION. PUBLIC SECTION. METHODS: constructor IMPORTING iv_name TYPE string iv_age TYPE i, get_info RETURNING VALUE(rv_info) TYPE string. PRIVATE SECTION. DATA: mv_name TYPE string, mv_age TYPE i.ENDCLASS.
CLASS lcl_person IMPLEMENTATION. METHOD constructor. mv_name = iv_name. mv_age = iv_age. ENDMETHOD.
METHOD get_info. rv_info = |{ mv_name } ist { mv_age } Jahre alt.|. ENDMETHOD.ENDCLASS.
" VerwendungDATA: lo_person TYPE REF TO lcl_person.
" Objekt mit Konstruktorparametern erzeugenlo_person = NEW #( iv_name = 'Max Müller' iv_age = 30 ).
WRITE: / lo_person->get_info( )." Ausgabe: Max Müller ist 30 Jahre alt.3. Methoden mit Parametern
CLASS lcl_calculator DEFINITION. PUBLIC SECTION. METHODS: add IMPORTING iv_a TYPE i iv_b TYPE i RETURNING VALUE(rv_result) TYPE i,
divide IMPORTING iv_dividend TYPE i iv_divisor TYPE i EXPORTING ev_result TYPE f RAISING cx_sy_zerodivide.ENDCLASS.
CLASS lcl_calculator IMPLEMENTATION. METHOD add. rv_result = iv_a + iv_b. ENDMETHOD.
METHOD divide. IF iv_divisor = 0. RAISE EXCEPTION TYPE cx_sy_zerodivide. ENDIF. ev_result = iv_dividend / iv_divisor. ENDMETHOD.ENDCLASS.
" VerwendungDATA: lo_calc TYPE REF TO lcl_calculator, lv_sum TYPE i, lv_quot TYPE f.
lo_calc = NEW #( ).
" RETURNING-Parameter: Funktionale Methodelv_sum = lo_calc->add( iv_a = 5 iv_b = 3 ).WRITE: / 'Summe:', lv_sum.
" EXPORTING-Parameterlo_calc->divide( EXPORTING iv_dividend = 10 iv_divisor = 4 IMPORTING ev_result = lv_quot ).WRITE: / 'Quotient:', lv_quot.4. Statische Methoden und Attribute (CLASS-DATA/CLASS-METHODS)
Statische Elemente gehören zur Klasse selbst, nicht zu einzelnen Objekten:
CLASS lcl_id_generator DEFINITION. PUBLIC SECTION. CLASS-METHODS: get_next_id RETURNING VALUE(rv_id) TYPE i. PRIVATE SECTION. CLASS-DATA: gv_counter TYPE i VALUE 0.ENDCLASS.
CLASS lcl_id_generator IMPLEMENTATION. METHOD get_next_id. gv_counter = gv_counter + 1. rv_id = gv_counter. ENDMETHOD.ENDCLASS.
" Verwendung - kein Objekt nötig!DATA: lv_id1 TYPE i, lv_id2 TYPE i.
lv_id1 = lcl_id_generator=>get_next_id( ). " => für statischen Aufruflv_id2 = lcl_id_generator=>get_next_id( ).
WRITE: / 'ID 1:', lv_id1. " 1WRITE: / 'ID 2:', lv_id2. " 25. Vererbung
Mit INHERITING FROM kann eine Klasse von einer anderen erben:
" BasisklasseCLASS lcl_vehicle DEFINITION. PUBLIC SECTION. METHODS: constructor IMPORTING iv_brand TYPE string, get_brand RETURNING VALUE(rv_brand) TYPE string. 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.ENDCLASS.
" Abgeleitete KlasseCLASS lcl_car DEFINITION INHERITING FROM lcl_vehicle. PUBLIC SECTION. METHODS: constructor IMPORTING iv_brand TYPE string iv_doors TYPE i, get_info RETURNING VALUE(rv_info) TYPE string. PRIVATE SECTION. DATA: mv_doors TYPE i.ENDCLASS.
CLASS lcl_car IMPLEMENTATION. METHOD constructor. " Konstruktor der Elternklasse aufrufen super->constructor( iv_brand = iv_brand ). mv_doors = iv_doors. ENDMETHOD.
METHOD get_info. rv_info = |{ mv_brand } mit { mv_doors } Türen|. ENDMETHOD.ENDCLASS.
" VerwendungDATA: lo_car TYPE REF TO lcl_car.
lo_car = NEW #( iv_brand = 'BMW' iv_doors = 4 ).WRITE: / lo_car->get_info( )." Ausgabe: BMW mit 4 Türen6. Methoden überschreiben (REDEFINITION)
CLASS lcl_animal DEFINITION. PUBLIC SECTION. METHODS: speak RETURNING VALUE(rv_sound) TYPE string.ENDCLASS.
CLASS lcl_animal IMPLEMENTATION. METHOD speak. rv_sound = 'Geräusch'. ENDMETHOD.ENDCLASS.
CLASS lcl_dog DEFINITION INHERITING FROM lcl_animal. PUBLIC SECTION. METHODS: speak REDEFINITION.ENDCLASS.
CLASS lcl_dog IMPLEMENTATION. METHOD speak. rv_sound = 'Wuff!'. ENDMETHOD.ENDCLASS.
" VerwendungDATA: lo_animal TYPE REF TO lcl_animal, lo_dog TYPE REF TO lcl_dog.
lo_animal = NEW lcl_animal( ).lo_dog = NEW lcl_dog( ).
WRITE: / 'Tier:', lo_animal->speak( ). " GeräuschWRITE: / 'Hund:', lo_dog->speak( ). " Wuff!Methodenparameter-Typen
| Typ | Beschreibung | Syntax |
|---|---|---|
IMPORTING | Eingabeparameter | IMPORTING iv_param TYPE typ |
EXPORTING | Ausgabeparameter | EXPORTING ev_param TYPE typ |
CHANGING | Ein- und Ausgabe | CHANGING cv_param TYPE typ |
RETURNING | Rückgabewert (funktional) | RETURNING VALUE(rv_param) TYPE typ |
RAISING | Mögliche Exceptions | RAISING cx_exception |
Objekterzeugung
DATA: lo_obj TYPE REF TO lcl_myclass.
" Klassische SyntaxCREATE OBJECT lo_obj.
" Mit ParameternCREATE OBJECT lo_obj EXPORTING iv_param = 'Wert'.
" Moderne Syntax (ab 7.40)lo_obj = NEW #( ).lo_obj = NEW #( iv_param = 'Wert' ).
" Inline-DeklarationDATA(lo_obj2) = NEW lcl_myclass( iv_param = 'Wert' ).Globale vs. Lokale Klassen
| Typ | Definition | Verwendung |
|---|---|---|
| Lokal | Im Programm mit CLASS ... DEFINITION | Nur im gleichen Programm |
| Global | Im Class Builder (SE24) | Systemweit verfügbar |
Lokale Klassen beginnen oft mit lcl_ (local class), globale mit zcl_ oder cl_.
Abstrakte Klassen und Methoden
Abstrakte Klassen können nicht instanziiert werden:
CLASS lcl_shape DEFINITION ABSTRACT. PUBLIC SECTION. METHODS: get_area ABSTRACT RETURNING VALUE(rv_area) TYPE f.ENDCLASS.
CLASS lcl_rectangle DEFINITION INHERITING FROM lcl_shape. PUBLIC SECTION. METHODS: constructor IMPORTING iv_width TYPE f iv_height TYPE f, get_area REDEFINITION. PRIVATE SECTION. DATA: mv_width TYPE f, mv_height TYPE f.ENDCLASS.
CLASS lcl_rectangle IMPLEMENTATION. METHOD constructor. mv_width = iv_width. mv_height = iv_height. ENDMETHOD.
METHOD get_area. rv_area = mv_width * mv_height. ENDMETHOD.ENDCLASS.Wichtige Hinweise / Best Practice
- Verwenden Sie sprechende Namen für Klassen und Methoden.
- Halten Sie Methoden kurz und fokussiert (Single Responsibility).
- Nutzen Sie PRIVATE für interne Daten, um Kapselung zu gewährleisten.
- RETURNING-Parameter ermöglichen funktionale Aufrufe und Verkettung.
- Für polymorphes Verhalten nutzen Sie
INTERFACEstatt Vererbung. - Behandeln Sie Fehler mit
TRY...CATCHund Exceptions. - Bevorzugen Sie die moderne Syntax
NEW #( )für Objekterzeugung. - Globale Klassen (SE24) sind wiederverwendbar und testbar.