ABAP CLASS und METHOD: Objektorientierte Programmierung

kategorie
ABAP-Statements
Veröffentlicht
autor
Johannes

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änglich
ENDCLASS.

Klassenimplementierung

CLASS <klassenname> IMPLEMENTATION.
METHOD <methode>.
" Methodenlogik
ENDMETHOD.
ENDCLASS.

Sichtbarkeitsbereiche

BereichZugriff
PUBLICVon überall zugänglich
PROTECTEDNur Klasse selbst und Unterklassen
PRIVATENur innerhalb der Klasse

Beispiele

1. Einfache Klasse definieren und verwenden

" Definition
CLASS 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.
" Implementierung
CLASS lcl_counter IMPLEMENTATION.
METHOD increment.
mv_count = mv_count + 1.
ENDMETHOD.
METHOD get_value.
rv_value = mv_count.
ENDMETHOD.
ENDCLASS.
" Verwendung
START-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: 2

2. 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.
" Verwendung
DATA: lo_person TYPE REF TO lcl_person.
" Objekt mit Konstruktorparametern erzeugen
lo_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.
" Verwendung
DATA: lo_calc TYPE REF TO lcl_calculator,
lv_sum TYPE i,
lv_quot TYPE f.
lo_calc = NEW #( ).
" RETURNING-Parameter: Funktionale Methode
lv_sum = lo_calc->add( iv_a = 5 iv_b = 3 ).
WRITE: / 'Summe:', lv_sum.
" EXPORTING-Parameter
lo_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 Aufruf
lv_id2 = lcl_id_generator=>get_next_id( ).
WRITE: / 'ID 1:', lv_id1. " 1
WRITE: / 'ID 2:', lv_id2. " 2

5. Vererbung

Mit INHERITING FROM kann eine Klasse von einer anderen erben:

" Basisklasse
CLASS 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 Klasse
CLASS 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.
" Verwendung
DATA: 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üren

6. 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.
" Verwendung
DATA: 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äusch
WRITE: / 'Hund:', lo_dog->speak( ). " Wuff!

Methodenparameter-Typen

TypBeschreibungSyntax
IMPORTINGEingabeparameterIMPORTING iv_param TYPE typ
EXPORTINGAusgabeparameterEXPORTING ev_param TYPE typ
CHANGINGEin- und AusgabeCHANGING cv_param TYPE typ
RETURNINGRückgabewert (funktional)RETURNING VALUE(rv_param) TYPE typ
RAISINGMögliche ExceptionsRAISING cx_exception

Objekterzeugung

DATA: lo_obj TYPE REF TO lcl_myclass.
" Klassische Syntax
CREATE OBJECT lo_obj.
" Mit Parametern
CREATE OBJECT lo_obj
EXPORTING
iv_param = 'Wert'.
" Moderne Syntax (ab 7.40)
lo_obj = NEW #( ).
lo_obj = NEW #( iv_param = 'Wert' ).
" Inline-Deklaration
DATA(lo_obj2) = NEW lcl_myclass( iv_param = 'Wert' ).

Globale vs. Lokale Klassen

TypDefinitionVerwendung
LokalIm Programm mit CLASS ... DEFINITIONNur im gleichen Programm
GlobalIm 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 INTERFACE statt Vererbung.
  • Behandeln Sie Fehler mit TRY...CATCH und Exceptions.
  • Bevorzugen Sie die moderne Syntax NEW #( ) für Objekterzeugung.
  • Globale Klassen (SE24) sind wiederverwendbar und testbar.