Cheat Sheet - Modernes ABAP

Cheat Sheet Modernes ABAP

Dieses Plakat ist die perfekte Ergänzung zu den Schulungen

von Brandeis Consulting

Die Sprache ABAP hat sich seit NetWeaver ABAP 7.40 enorm weiterentwickelt. Viele neue Sprachkonstrukte sind hinzugekommen. Vor allem natürlich viele neue Ausdrücke. Damit kann man sauberen Code schreiben und sich viele Hilfsvariablen und Deklarationen sparen. Die neuen Sprachelemente sollten genutzt werden, um den Code robuster und lesbarer zu machen. Eine schnellere Ausführung ist jedoch nicht zu erwarten.

Dieses Plakat zeigt die wichtigsten und nützlichsten Neuerungen. Ausgelassen wurden die neue ABAP-SQL-Syntax und die ABAP Built-In Functions.

Inline-Deklarationen

Mit Inline-Deklarationen kann eine Variable dort definiert werden, wo sie zum ersten Mal verwendet wird, d.h. wo ihr zum ersten Mal ein Wert zugewiesen wird. An vielen Stellen im ABAP-Code ergibt sich der Datentyp einer Variablen aus dem Kontext.

Mit DATA(<variablenname>) kann eine Variable dort definiert werden, wo sie benötigt wird.

Modernes ABAP

LOOP AT ResultTab INTO DATA(ResultLine). 
    ...
ENDLOOP. 
 
  

Klassisches ABAP

DATA ResultLine LIKE LINE OF ResultTab. 

LOOP AT ResultTab INTO ResultLine. 
    ...
ENDLOOP. 

Die Vorteile der Inline-Deklaration sind neben dem kompakteren Code auch die Flexibilität. Wenn sich im Beispiel die Feldliste ändert, wird der Datentyp von RESULT automatisch angepasst. Gerade bei JOIN Operationen hat man oft auch keine passende Strukturdefinition.

Modernes ABAP

SELECT * 
  FROM zbc_users
 INTO TABLE @DATA(Result). 
 
 

Klassisches ABAP

DATA Result TYPE TABLE OF zbc_users.

SELECT * 
  FROM zbc_users
 INTO TABLE Result. 

Auch Feldsymbole können mit Inline-Deklaration einem existierenden Speicherbereich zugewiesen werden. Entweder mit einem ASSIGN oder mit dem den passenden Anweisungen für den Zugriff auf interne Tabellen wie zum Beispiel

LOOP AT ... ASSIGNING FIELD-SYMOBL(<fs-name>). oder

READ TABLE ... ASSIGNING FIELD-SYMBOL(<fs-name>) ...

Verkettungsoperator &&

Zwei Zeichenketten können mit dem Operator && verkettet werden. Dies ist wesentlich eleganter als mit CONCATENATE und funktioniert auch an Operandenpositionen ohne Hilfsvariable.

Modernes ABAP

out->write( `Es ist das Jahr ` 
                && sy-datum(4) ). 
  

Klassisches ABAP

DATA tmp TYPE c LENGTH 100.
CONCATENATE `Es ist das Jahr ` 
            sy-datum(4) INTO tmp.
out->write( tmp ).             

Berechnungszuweisung mit &&=

Beim Zusammensetzen von Zeichenketten ist es oft notwendig, 'Anhängen' zu verwenden. Zum Beispiel bei der Generierung von HTML.

Modernes ABAP

html &&= `<b>Hallo</b>`

Klassisches ABAP

CONCATENATE html `<b>Hallo</b>`
       INTO html. 

String-Templates

String-Templates sind Ausdrücke, die eine Zeichenkette erzeugen. Sie bestehen aus Literalen, die eingebettete Ausdrücke, Formatierungen und Steuerzeichen enthalten können. Ein String-Template beginnt und endet mit einem senkrechten Strich |. Alles dazwischen ist konstanter Text (=Literal), außer er ist ein von geschweiften Klammern { ... } umgebener, eingebetter Ausdruck.

Die Zeichen {, }, | und \ müssen im Literal durch einen Backslash \ escaped werden.

Modernes ABAP

out->write( |Heute: { 
    sy-datum DATE = USER }| ).





Klassisches ABAP

DATA tmp   TYPE string.
DATA datum TYPE c LENGTH 10.

WRITE sy-datum TO datum.
CONCATENATE `Heute: ` datum INTO tmp.

out->write( tmp ). 

Ausdrücke in String Templates

In geschweiften Klammern { ... } können Ausdrücke direkt in das String-Template eingebettet werden. Damit kann z.B. eine funktionale Methode aufgerufen oder eine Berechnung durchgeführt werden. Schöne Beispiele dafür sind bei dem Konstruktor-Ausdrücken COND und SWITCH.

Formatierung

Die Darstellung der Ausdrücke kann über die Formatierungsoptionen angepasst werden. Die wichtigsten sind

  • WIDTH = <Länge> - Breite in Zeichen
  • ALIGN = <Ausrichtung> - Entweder LEFT, RIGHT oder CENTER
  • DATE = USER - Formatierung des Datums gemäß Benutzerstammsatz, siehe oben
  • TIME = USER - Dasselbe für die Zeit
  • ALPHA = IN/OUT - Alphakonvertierung

Steuerzeichen in String Templates

Die Steuerzeichen

  • \n - Zeilenvorschub/Line Feed
  • \r - Wagenrücklauf/Return und
  • \t - Tabulator

können direkt in String Templates verwendet werden. Damit können also ohne weiteres auch mehrzeilige Texte ohne die Verwendung von CL_ABAP_CHAR_UTILS=>CR_LF erzeugt werden:

out->write( |Hallo\r\nWelt| ).

Aufzählungstypen mit Enumerations

Mit den Enumerations haben wir typsichere Aufzählungstypen. Das bedeutet, dass mit der Enumeration auch ein Datentyp definiert wird. Und es wird zur Designzeit geprüft wird, ob der Datentyp bei Zuweisungen und Methodenaufrufen korrekt ist. Somit können nur die Werte verwendet werden, die definiert wurden.

Definition einer Aufzählung

TYPES: BEGIN OF ENUM eColour,
          Red,
          ...
          Orange,
          Violet,
        END OF ENUM eColour.

Verwendung der Aufzählung

DATA MyColour TYPE eColor.

" MyColour = 0. "Gibt einen Fehler
MyColour = Red .


Gruppierung in einer Struktur

Da manchmal viele konstante Werte definiert werden müssen, ist es möglich, die einzelnen Komponenten einer Enumeration in einer Struktur zusammenzufassen. Dies erleichtert das Auffinden der richtigen Werte. Der Zugriff erfolgt wie bei einer konstanten Struktur: MyColour = Colour-Red

Definition mit Struktur

TYPES: 
  BEGIN OF ENUM eColour STRUCTURE Colour,
    Red,
    ...
    Orange,
    Violet,
  END OF ENUM eColour STRUCTURE Colour.

Feste Werte und andere Datentypen

Normalerweise wird intern der Datentyp I verwendet und die Werte werden von 0 bis N zugewiesen. Dies kann aber auch manuell geändert werden. Der Datentyp kann bis zu 8 Zeichen lang sein. Wichtig ist auch, dass für eine Konstante immer der Wert IS INITIAL verwendet wird.

TYPES ColourDef TYPE c LENGTH 7.
TYPES: BEGIN OF ENUM eColour
            STRUCTURE Colour
              BASE TYPE ColourDef,

         Black  VALUE IS INITIAL,
         Red    VALUE '#ff0000',
         Green  VALUE '#00ff00',
         Blue   VALUE '#0000ff',

       END OF ENUM eColour
            STRUCTURE Colour.

Wert und Enum-Typ

Da eine direkte Wertzuweisung nicht möglich ist bzw. in einer Richtung zum falschen Ergebnis führt, ist für die Speicherung der Werte in der Datenbank (bzw. für alle Schnittstellen) immer eine Konvertierung mit dem Konstruktor-Operator CONV erforderlich:

DATA MyColour TYPE eColour.
DATA ColourHex TYPE ColourDef.

"Falscher Inhalt 'GREEN':
ColourHex = Colour-green.

"Richtiger Inhalt  '#00ff00' :
ColourHex = conv #( Colour-Green ).

"Syntaxfehler:
" MyColour = '#00ff00'.

"Korrekte Zuweisung:
MyColour = conv #( '#00ff00' ). 

Konstruktor-Ausdrücke

Konstruktor-Ausdrücke erzeugen ein neues (Daten-)Objekt eines bestimmten (Daten-)Typs. Dazu werden sogenannte Konstruktor-Operatoren verwendet. Diese decken eine Vielzahl sehr unterschiedlicher Anwendungsfälle ab. Gemeinsam ist ihnen jedoch immer folgende Syntax:

<Konstruktor-Operator> <Datentyp>( <Parameter> ).

Ein einfaches Beispiel: Mit dem Operator VALUE eine interne Tabelle mit festem Inhalt erzeugen.

DATA tt_tadir TYPE STANDARD TABLE OF tadir.
DATA ObjectCatalog TYPE tt_tadir.

ObjectCatalog = VALUE tt_tadir( ( obj_name = 'ZCL_CS' object = 'CLAS' )
                                ( obj_name = 'MARA'   object = 'TABL' ) ).

Der Datentyp und das Hash-Zeichen #

Je nach Situation wird der passende Datentyp angegeben, z.B. ein Datenelement, ein Tabellentyp, ein Strukturtyp oder ein Klassenname.

Wenn der Datentyp aus dem Kontext heraus abgeleitet werden kann, dann muss er nicht mehr angegeben werden. Stattdessen wird das # Zeichen verwendet. Das ist z.B. bei einer Zuweisung der Fall.

Lesbarkeit von Konstruktor-Ausdrücken

Einige Operatoren können sehr komplexe Logik ausführen. Dazu können Konstruktor-Ausdrücke auch ineinander verschachtelt werden. Der Code wird dabei schnell unübersichtlich. Es empfiehlt sich daher, nur einfache Logik in Konstruktor-Ausdrücken zu berechnen. Bei komplexer Logik ist klassisches ABAP oft besser lesbar. In den ADTs gibt es keine Möglichkeit, Konstruktor-Ausdrücke zu debuggen.

Daten und Objekte erzeugen mit VALUE und NEW

Die beiden Operatoren erzeugen Daten. Mit VALUE erhält man die Daten direkt, mit NEW die Referenz darauf. Wenn keine Parameter übergeben werden, erzeugen beide Operatoren leere Datenobjekte.

Strukturen erzeugen

Wenn Strukturen erzeugt werden, kann man die einzelnen Komponenten als Parameter mitgeben. Nicht zugewiesene Komponenten werden mit ihrem Initialwert belegt.

DATA(line) = VALUE tadir( obj_name = 'ZCL_CS' object = 'CLAS' ). 

Interne Tabellen erzeugen

Die einzelnen Zeilen einer internen Tabelle werden wiederum in runde Klammern eingeschlossen.

DATA DateRange TYPE RANGE OF dats.

DateRange = VALUE #( ( sign = 'I' option = 'EQ'   low = '20221031'  )
                     ( sign = 'I' option = 'EQ'   low = '20220406'  ) ).

Gemeinsame Bestandteile der einzelnen Zeilen können auch vor den runden Klammern definiert werden:

DateRange = VALUE #( sign = 'I' option = 'EQ'  ( low = '20221031'  )
                                               ( low = '20230406'  ) ).

BASE gibt einen Initialwert vor

Bei Strukturen kann vor der ersten Komponente mit BASE eine kompatible Struktur angegeben werden, die die nicht zugewiesenen Komponenten mit Werten füllt.

Bei internen Tabellen kann mit BASE eine interne Tabelle mitgegeben werden, die dann durch die folgenden Zeilen ergänzt wird. Das entspricht also einem APPEND.

NewDateRange = VALUE #( BASE DateRange
                      ( sign = 'I' option = 'EQ'   low = '20230101'  ) ).

Im VALUE-Operator sind auch FOR-Schleifen möglich. Diese sind beim REDUCE-Operator beschrieben.

Objekte erzeugen mit NEW

Mit NEW können auch Instanzen von Klassen erzeugt werden:

NEW <Klassenname>( <Konstruktorparameter> )

SAP Dokumentation VALUE und NEW

Datentypänderung mit CONV

Dieser Operator wird verwendet, um einfach Datentypen zu konvertieren. Dies ist z.B. bei Methodenparametern ein häufiges Problem: Der Inhalt einer Variablen passt, der Datentyp aber nicht.

Modernes ABAP

my_method( TEXT = CONV #( sy-datum ) ).


Klassisches ABAP

DATA DateText TYPE char8. 
DateText = sy-datum. 
my_method( Text =  DateText )

Weitere Beispiel finden Sie bei den Enumerations.

Down- und Upcast von Referenzvariablen mit CAST

Im klassischen ABAP erfolgt der Upcast durch einfache Zuweisung mit =, der Downcast mit dem Cast-Operator ?=. Dafür wird immer eine Hilfsvariable vom passenden Typ benötigt. Mit dem CAST-Operator kann diese in vielen Fällen entfallen:

Modernes ABAP

DATA Task TYPE zbc_tasks.     

DATA(Components) = cast cl_abap_structdescr( 
                          cl_abap_typedescr=>describe_by_data( 
                              Task ) )->get_components(  ).

Klassisches ABAP

DATA Task TYPE zbc_tasks.
DATA StructDescr TYPE REF TO cl_abap_structdescr.
DATA(TypeDescr) = cl_abap_typedescr=>describe_by_data( Task ).

StructDescr ?= TypeDescr.
DATA(Components) = StructDescr->get_components(  ).

Fallunterscheidungen mit COND und SWITCH

Diese Konstruktor-Ausdrücke entsprechen im SQL dem CASE Ausdruck. Einfache Fallunterscheidungen in Abhängigkeit von einem Feld werden mit SWITCH implementiert:

Modernes ABAP

out->write( 
  |Hallo { SWITCH #( User-Gender
                    WHEN 'F' THEN 'Frau'
                    WHEN 'M' THEN 'Herr'
                    ELSE '' )
              } { User-Lastname } | )..



              

Klassisches ABAP

DATA Salutation TYPE string.

CASE User-Gender.
  WHEN 'F'.    Salutation = 'Frau'.
  WHEN 'M'.    Salutation = 'Herr'.
  WHEN OTHERS. Salutation = ''.
ENDCASE.

out->write(  |Hallo { Salutation  
                  } { User-Lastname }| ).

Komplexere Unterscheidungen mit beliebigen Bedingungen erfolgen mit COND:

out->write( |Der Status ist { COND #( WHEN Priority > 3 
                                       AND DueDate <= SY-DATUM  THEN 'Critical'
                                      WHEN Priority > 2         THEN 'Medium'
                                                                ELSE 'Low' ) }|.

SAP Dokumentation COND und SWITCH

Mit REDUCE einen Wert aus einer internen Tabelle berechnen

Dieser Operator berechnet durch Schleifen über interne Tabellen einen einzelnen Ergebniswert. Auf komplexe Varianten wurde bewusst verzichtet, da dieser Operator schnell die Lesbarkeit einschränkt.

out->write( REDUCE string( INIT res TYPE string
                                sep TYPE string
                            FOR User IN Users
                            NEXT
                              res &&= sep && User-Firstname
                              sep = ', ' ) ).

Interne Tabellen mit dem FILTER-Operator erzeugen

Mit dem FILTER-Operator können interne Tabellen auf Basis einer anderen internen Tabelle durch Filterung erzeugt werden. Entweder über eine einfache WHERE-Klausel oder anhand einer anderen Tabelle. Grundsätzlich muss der Tabellentyp und -Schlüssel für die Filterung optimiert sein, sonst gibt es Syntaxfehler.

FILTER mit WHERE-Klausel

DATA lt_data TYPE sorted TABLE OF I_CountryText WITH UNIQUE KEY LANGUAGE  COUNTRY.
SELECT * FROM i_countrytext INTO TABLE @lt_data.

out->WRITE( FILTER #( lt_data WHERE LANGUAGE = 'D' ) ).

FILTER mit IN ... WHERE

Hier wird nach einer anderen internen Tabelle gefiltert. Dies entspricht einem INNER JOIN in SQL.

DATA lt_data TYPE sorted TABLE OF I_CountryText WITH UNIQUE KEY COUNTRY.    
DATA(lt_filter) = VALUE tt_demo( ( COUNTRY = 'DE' )
                                  ( COUNTRY = 'US' ) ).
SELECT * FROM i_countrytext WHERE LANGUAGE = 'D' INTO TABLE @lt_data.

out->WRITE( FILTER #( lt_data IN lt_filter WHERE COUNTRY = COUNTRY  ) ).

Der Operator CORRESPONDING

Dieser Operator erinnert an die Anweisung MOVE-CORRESPONDING. Er kann sowohl für Strukturen als auch für interne Tabellen verwendet werden. Mit dem CORRESPONDING-Operator kann man aus einem strukturierten Datenobjekt, also einer internen Tabellen oder einer Struktur, ein anderes Datenobjekt erzeugen und dabei die Werte gleichlautender Felder übernehmen. Das ähnelt der Anweisung MOVE-CORRESPONDING.

Grundform von CORRESPONDING

Die Daten werden in gleichlautende Felder kopiert. Felder, die in der Quelle nicht vorhanden sind, bleiben im Ziel leer.

TasksSmall = corresponding #( TasksOriginal ).

Explizites MAPPING und EXCEPT

Wenn die Komponenten nicht exakt den gleichen Namen haben und trotzdem aufeinander kopiert werden sollen oder wenn einzelne Komponenten eben nicht kopiert werden sollen, so kann dies explizit mit den Zusätzen MAPPING und EXCEPT angegeben werden:

TaskSmall = corresponding #( TaskOriginal
                                MAPPING id    = task_id
                                        title = summary
                                EXCEPT assignee ).

Mit BASE werden Initialwerte vorgegebenen, siehe VALUE-Operator. Damit werden bei Strukturen die Werte vorgefüllt oder bei internen Tabellen zusätzliche Zeilen an den Anfgang gesetzt.

CORRESPONDING mit Lookup Tabelle

Diese Variante führt ein Lookup auf eine andere interne Tabelle durch. Das entspricht einem LEFT OUTER JOIN mit einer :1 Kardinalität. Wie beim FILTER-Operator funktioniert dies nur dann, wenn der Tabellentyp und die Schlüsseldefinition zu der Join-Bedingung in der USING-Klausel passt.

DATA Lookup TYPE HASHED TABLE OF I_CountryText WITH UNIQUE KEY Country.
DATA(Original) = VALUE tt_demo( ( Country = 'DE' )
                                ( Country = 'US' ) ).
SELECT * FROM I_CountryText WHERE LANGUAGE = 'D' INTO TABLE @Lookup.

DATA(Result) = CORRESPONDING tt_demo( Original FROM Lookup
                                      USING Country        = Country
                                      MAPPING country_text = CountryName  ).

Prädikativer Methodenaufruf

Hinter dem sperrigen Begriff Prädikativer Methodenaufruf verbirgt sich ein einfaches Konzept, das in (fast) allen anderen Programmiersprachen Standard ist: Ein Methodenaufruf als Prädikat, z.B. direkt in einer IF Anweisung. Da im ABAP die Werte TRUE und FALSE nicht eindeutig definiert sind, gilt die folgende Definition: Wenn der Rückgabewert einer funktionalen Methode (d.h. mit RETURNING ) initial ist, ist das Prädikat logisch FALSE, ansonsten TRUE. Es entspricht also IF Methode( ) IS NOT INITIAL.

Modernes ABAP

IF isRelevant( ).
  ...
ENDIF.

Klassisches ABAP

IF isRelevant( ) EQ abap_true.
  ...
ENDIF.

Zugriff auf Tabellenzeilen mit Tabellenausdrücken

Auch wenn der Name etwas anderes vermuten lässt, so liefern uns Tabellenausdrücke Zeilen einer Tabelle. In SQL würde man sie daher Zeilenausdrücke nennen.

Sie sind keine Kopien der Zeile, sondern die Zeile in der Tabelle. Deshalb verändern Schreiboperationen auch die Tabelle. Sie ersetzen also READ ... INTO und READ ... ASSIGNING Anweisungen.

Aufbau von Tabellenausdrücken: <Tabelle>[ <Zeilenspezifikation> ]

Die Tabelle kann dabei eine beliebige interne Tabelle sein. Die Zeilenspezifikation steht in eckigen Klammern und kann auf verschiedene Arten erfolgen: Über die Angabe der Zeilennummer bei Standardtabellen, eines freien Schlüssels oder des Tabellenschlüssels.

Modernes ABAP

* Kopie der ersten Zeile 
DATA(row) = users[ 1 ]. 
 
* Ändern des Vornamens 1. Zeile
users[ 1 ]-firstname = 'Edgar'.   


* Ändern von Peters Nachname
users[ firstname = 'Peter'
          ]-lastname = 'Pan'. 

Klassisches ABAP

DATA row LIKE LINE OF users.
READ TABLE users  INTO row INDEX 1.

FIELD-SYMBOLS <row> LIKE LINE OF users. 
READ TABLE users ASSIGNING <row> INDEX 1. 
<row>-firstname = 'Edgar'. 

FIELD-SYMBOLS <row>  LIKE LINE OF users. 
READ TABLE users ASSIGNING <row> 
           WITH firstname = 'Peter'. 
<row>-lastname = 'Pan'. 

Ein Zugriff auf nicht existierende Zeilen erzeugt eine Ausnahme CX_SY_ITAB_LINE_NOT_FOUND.

Modernes ABAP

* Ändern von Peters Nachname
TRY.
  users[ firstname = 'Peter' 
               ]-lastname ='Pan'. 
CATCH CX_SY_ITAB_LINE_NOT_FOUND. 

ENDTRY. 

Klassisches ABAP

* Ändern von Peters Nachname
FIELD-SYMBOLS <row>  LIKE LINE OF users. 
READ TABLE users ASSIGNING <row> 
           WITH firstname = 'Peter'. 
IF sy-subrc = 0. 
  <row>-lastname = 'Pan'.
ENDIF.  

Bei lesendem Zugriff kann alternativ auch mit dem VALUE-Operator ein Defaultwert erzeugt werden oder der Zugriff als OPTIONAL markiert werden.

...VALUE #( <Tabelle>[<Zeilenspezifikation>] 
             DEFAULT <Alternativer Wert> | OPTIONAL )

Neue Gruppenstufenverarbeitung

Die klassische Gruppenstufenverarbeitung mit AT ... ist abhängig von der Sortierung der Daten und der Reihenfolge der Spalten. Beides muss exakt passen. Damit ist sie fehleranfällig und manchmal kaum nutzbar. Die neue Gruppenstufenverarbeitung löst dieses Problem durch verschachtelte LOOPs.

Modernes ABAP

LOOP AT PlantMats 
     INTO DATA(Grouping)
     GROUP BY ( Plant = Grouping-Plant )
     INTO DATA(Grp).                     
" A            
  LOOP AT GROUP Grp
          INTO DATA(PlantMat). 
" B   
  ENDLOOP.         
" C
ENDLOOP. 

Klassisches ABAP

LOOP AT PlantMats 
     INTO DATA(PlantMat).
  AT NEW Plant.
" A
  ENDAT.
" B
  AT END OF Plant.
" C
  ENDAT.

ENDLOOP.

Brandeis Consulting

Massgeschneiderte Schulungen und Beratung von Buchautoren und SAP Champions! Auf unserer Website www.brandeis.de finden Sie unser Angebot!

(C) Brandeis Consulting GmbH