Cheat Sheet - Modern ABAP

Cheat Sheet Modern ABAP

This poster is the perfect complement to the training courses

by Brandeis Consulting

The ABAP language has evolved tremendously since NetWeaver ABAP 7.40. Many new language constructs have been added. Especially, of course, many new expressions. With them you can write clean code and save many auxiliary variables and declarations. The new language elements should be used to make the code more robust and readable. However, faster execution is not to be expected.

This poster shows the most important and useful new features. Omitted are the new ABAP SQL syntax and ABAP Built-In Functions.

Inline Declarations

With inline declarations, a variable can be defined where it is used for the first time, that is, where a value is assigned to it for the first time. In many places in ABAP code, the data type of a variable results from the context.

With DATA(<variable name>) a variable can be defined where it is needed.

Modern ABAP

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

Classic ABAP

DATA ResultLine LIKE LINE OF ResultTab. 

LOOP AT ResultTab INTO ResultLine. 
    ...
ENDLOOP. 

The main advantage of the inline declaration is, besides the more compact code, more flexibility. If the field list changes in the example, the data type of RESULT is automatically adjusted. Especially with JOIN operations you often don`t have a suitable structure definition either.

Modern ABAP

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

Classic ABAP

DATA Result TYPE TABLE OF zbc_users.

SELECT * 
  FROM zbc_users
 INTO TABLE Result. 

Field symbols can also be assigned to an existing memory area with inline declarations. Either with an ASSIGN or with the appropriate statements for accessing internal tables, such as

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

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

Concatenation Operator &&

Two strings can be concatenated using the && operator. This is much more elegant than using CONCATENATE and also works at operand positions without an auxiliary variable.

Modern ABAP

out->write( `It is the year ` 
                && sy-datum(4) ). 
  

Classic ABAP

DATA tmp TYPE c LENGTH 100.
CONCATENATE `It is the year ` 
            sy-date(4) INTO tmp.
out->write( tmp ).             

Calculation assignment with &&=

When composing strings, it is often necessary to use 'append'. For example, when generating HTML.

Modern ABAP

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

Classic ABAP

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

String Templates

String Templates are expressions that generate a string of characters. They consist of liters that can contain embedded expressions, formatting, and control characters. A string template starts and ends with a vertical bar |. Everything in between is constant text (=literal), unless it is an embedded expression surrounded by curly braces { ... }.

The characters {, }, | and \ must be escaped in the literal by a backslash \.

Modern ABAP

out->write( |Today: { 
    sy-date DATE = USER }| ).





Classic ABAP

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

WRITE sy-date TO theDate.
CONCATENATE `Today: ` theDate INTO tmp.

out->write( tmp ). 

Expressions in String Templates

In curly braces { ... }, expressions can be embedded directly into the string template. This can be used to call a functional method or perform a calculation, for example. Nice examples of this can be found in the box about the constructor expressions COND and SWITCH.

Formatting

The appearance of the expressions can be customized using the formatting options. The most important ones are

  • WIDTH = <length> - width in characters
  • ALIGN = <alignment> - Either LEFT, RIGHT or CENTER
  • DATE = USER - format the date according to the user master record, see above
  • TIME = USER - The same for the time
  • ALPHA = IN/OUT - Alpha conversion

Control characters in String Templates

The control characters

  • \n - Line Feed
  • \r - Carriage Return and
  • \t - Tabulator

can be used directly in String Templates. So you can easily create multiline texts without using CL_ABAP_CHAR_UTILS=>CR_LF:

out->write( |Hello\r\nWorld| ).

Enumerations

With enumerations, we have type-safe enumeration types. This means that with the enumeration, a datatype is also defined. And it is checked at design time if the data type is correct for assignments and method calls. So only the values that have been defined can be used.

Definition of an Enumeration

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

Enumeration usage

DATA MyColour TYPE eColor.

" MyColour = 0. "Gives an error.
MyColour = Red .


Grouping in a structure

Since sometimes many constant values need to be defined, it is possible to group the individual components of an enumeration in a structure. This makes it easier to find the correct values. The access is the same as for a constant structure: MyColour = Colour-Red

definition with structure

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

Fixed values and other data types.

Normally the data type I is used internally and values are assigned from 0 to N. However, this can also be changed manually. The data type can be up to 8 characters long. It is also important that for a constant always the value IS INITIAL is used.

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 Color.

Value and enum type

Since a direct value assignment is not possible or leads to the wrong result in one direction, a conversion with the constructor operator CONV is always required for storing the values in the database (or for all interfaces):

DATA MyColour TYPE eColour.
DATA ColourHex TYPE ColourDef.

"Wrong content 'GREEN':
ColourHex = Colour-green.

" Correct content '#00ff00' :
ColourHex = conv #( Colour-Green ).

" Syntax error:
" MyColour = '#00ff00'.

" Correct assignment:
MyColour = conv #( '#00ff00' ). 

Constructor Expressions

Constructor expressions create a new (data) object of a specific (data) type. So-called constructor operators are used for this purpose. These cover a variety of very different use cases. However, they always have the following syntax in common:

<ConstructorOperator> <DataType>( <Parameter> ).

A simple example: use the VALUE operator to create an internal table with fixed contents.

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' ) ).

The data type and the hash character #

Depending on the situation, the appropriate data type is specified, e.g. a data element, a table type, a structure type or a class name.

If the data type can be derived from the context, then it does not need to be specified. Instead, the # character is used. This is the case, for example, with an assignment.

Readability of constructor expressions

Some operators can perform very complex logic. For this purpose, constructor expressions can also be nested within each other. The code quickly becomes confusing. It is therefore recommended to calculate only simple logic in constructor expressions. For complex logic, classic ABAP is often more readable. There is no way to debug constructor expressions in ADTs.

Create data and objects with VALUE and NEW

The two operators create data. With VALUE you get the data directly, with NEW you get the reference to it. If no parameters are passed, both operators create empty data objects.

Create structures

If structures are created, the individual components can be passed as parameters. Unassigned components are assigned their initial value.

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

Create internal tables

The individual lines of an internal table are again enclosed in round brackets.

DATA DateRange TYPE RANGE OF dats.

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

Common components of the individual lines can also be defined before the round brackets:

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

BASE specifies an initial value

For structures, a compatible structure can be specified before the first component with BASE, which fills the unassigned components with values.

For internal tables an internal table can be specified with BASE, which is then supplemented by the following lines. So this corresponds to an APPEND.

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

In the VALUE operator FOR loops are also possible. These are described in the REDUCE operator.

Create objects with NEW

Instances of classes can also be created with NEW:

NEW <class name>( <constructor parameter> )

SAP documentation VALUE and NEW

Data type change with CONV

This operator is used to easily convert data types. This is a common problem with method parameters, for example: the content of a variable matches, but the data type does not.

Modern ABAP

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


Classic ABAP

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

See the enumerations for more examples.

Down- and upcast of reference variables with CAST

In classic ABAP, the upcast is done by simple assignment with =, the downcast with the cast operator ?=. This always requires an auxiliary variable of the appropriate type. With the CAST operator, this can be omitted in many cases:

Modern ABAP

DATA Task TYPE zbc_tasks.     

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

Classic 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( ).

Case distinctions with COND and SWITCH

These constructor expressions correspond to the CASE expression in SQL. Simple case distinctions depending on a single field are implemented with SWITCH:

Modern ABAP

out->write( 
  |Hello { SWITCH #( User-Gender
                    WHEN 'F' THEN 'Mrs.'
                    WHEN 'M' THEN 'Mr.'
                    ELSE '' )
              } { User-Lastname } | ).



              

Classic ABAP

DATA salutation TYPE string.

CASE user-gender.
  WHEN 'F'.    Salutation = 'Mrs.'.
  WHEN 'M'.    Salutation = 'Mr'.
  WHEN OTHERS. Salutation = ''.
ENDCASE.

out->write( |Hello { Salutation  
                  } { user load name }| ).
.

More complex distinctions with arbitrary conditions are made with COND:

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

SAP documentation COND and SWITCH

Use REDUCE to calculate a value from an internal table.

This operator calculates a single result value by looping over internal tables. Complex variants have been deliberately omitted, as this operator quickly limits readability.

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

Create Internal Tables with the FILTER operator

The FILTER operator can be used to create internal tables based on another internal table by filtering. Either via a simple WHERE clause or based on another table. Basically, the table type and key must be optimized for filtering, otherwise there will be syntax errors.

FILTER with WHERE clause

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 with IN ... WHERE

Here we filter for another internal table. This corresponds to an 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 ) ).

The CORRESPONDING Operator.

This operator is reminiscent of the MOVE-CORRESPONDING statement. It can be used for structures as well as for internal tables. The CORRESPONDING operator can be used to create another data object from a structured data object, i.e. an internal table or a structure, and to take over the values of identical fields. This is similar to the MOVE-CORRESPONDING statement.

Basic form of CORRESPONDING

The data is copied to fields with the same names. Fields that do not exist in the source remain empty in the destination.

TasksSmall = corresponding #( TasksOriginal ).

Explicit MAPPING and EXCEPT

If the components do not have exactly the same name but should still be copied to each other, or if individual components should not be copied, this can be specified explicitly with the MAPPING and EXCEPT additions:

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

With BASE initial values are given, see VALUE operator. This is used to prefill the values for structures or set additional lines at the beginning for internal tables.

CORRESPONDING with lookup table

This variant performs a lookup to another internal table. This corresponds to a LEFT OUTER JOIN with a :1 cardinality. As with the FILTER operator, this only works if the table type and key definition match the join condition in the USING clause.

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 ).

Predicative Method Calls

Behind the unwieldy term Predicative Method Call lies a simple concept that is standard in (almost) all other programming languages: a method call as a predicate, e.g. it is used directly in an IF statement. Since in ABAP the values TRUE and FALSE are not clearly defined, the following definition applies: If the return value of a functional method (i.e. with RETURNING ) is initial, the predicate is logically FALSE, otherwise TRUE. Thus, it corresponds to IF method( ) IS NOT INITIAL.

Modern ABAP

IF isRelevant( ).
  ...
ENDIF.

Classic ABAP

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

Table Expressions

Even if the name suggests otherwise, table expressions provide us a row of a table. In SQL, therefore, they would be called row expressions.

They are not copies of the row, but the row in the table. Therefore, write operations on table expressions also modify the table. So they can replace READ ... INTO and READ ... ASSIGNING statements.

Construction of table expressions: <table>[ <RowSpecification> ]

The table can be any internal table. The row specification is enclosed in square brackets and can be done in several ways: By specifying the row number for standard tables, a free key or the table key.

Modern ABAP

* Copy of the first row 
DATA(row) = users[ 1 ]. 
 
* Change the first name of the 1st row
users[ 1 ]-firstname = 'Edgar'.   


* Change Peter's last name
users[ firstname = 'Peter'
          ]-lastname = 'Pan'. 

Classic 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'. 

Accessing non-existent lines will generate an exception CX_SY_ITAB_LINE_NOT_FOUND.

Modern ABAP

* Changing Peter's last name
TRY.
  users[ firstname = 'Peter' 
               ]-lastname = 'Pan'. 
CATCH CX_SY_ITAB_LINE_NOT_FOUND. 

ENDTRY. 

Classic ABAP

* Changing Peter's last name
FIELD SYMBOLS <row> LIKE LINE OF users. 
READ TABLE users ASSIGNING <row>. 
           WITH firstname = 'Peter'. 
IF sy-subrc = 0. 
  <row>-lastname = 'Pan'.
ENDIF.  

Alternatively, for read access, use the VALUE operator to create a default value or mark the access as OPTIONAL.

...VALUE #( <table>[<RowSpecification>] 
             DEFAULT <AlternativeValue> | OPTIONAL )

New Group Level Processing

The classic group level processing with AT NEW/END depends on the sorting of the data and the order of the columns. Both must fit exactly. Thus it is error-prone and sometimes hardly usable. The new group level processing solves this problem by nested LOOPs.

Modern 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. 

Classic ABAP

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

ENDLOOP.

Brandeis Consulting

Customized training and consulting from book authors and SAP champions! On our website www.brandeis.de you will find our offer!

(C) Brandeis Consulting Ltd.