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.
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.
LOOP AT ResultTab INTO DATA(ResultLine).
...
ENDLOOP.
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.
SELECT *
FROM zbc_users
INTO TABLE @DATA(Result).
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>) ...
&&
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.
out->write( `It is the year `
&& sy-datum(4) ).
DATA tmp TYPE c LENGTH 100.
CONCATENATE `It is the year `
sy-date(4) INTO tmp.
out->write( tmp ).
When composing strings, it is often necessary to use 'append'. For example, when generating HTML.
html &&= `<b>Hello</b>`
CONCATENATE html `<b>Hello</b>`
INTO html.
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 \
.
out->write( |Today: {
sy-date DATE = USER }| ).
DATA tmp TYPE string.
DATA theDate TYPE c LENGTH 10.
WRITE sy-date TO theDate.
CONCATENATE `Today: ` theDate INTO tmp.
out->write( tmp ).
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
.
The appearance of the expressions can be customized using the formatting options. The most important ones are
WIDTH = <length>
- width in charactersALIGN = <alignment>
- Either LEFT
, RIGHT
or CENTER
DATE = USER
- format the date according to the user master record, see aboveTIME = USER
- The same for the timeALPHA = IN/OUT
- Alpha conversionThe 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| ).
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.
TYPES: BEGIN OF ENUM eColour,
Red,
...
Orange,
Violet,
END OF ENUM eColour.
DATA MyColour TYPE eColor.
" MyColour = 0. "Gives an error.
MyColour = Red .
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
TYPES:
BEGIN OF ENUM eColour STRUCTURE Colour,
Red,
...
Orange,
Violet,
END OF ENUM eColour STRUCTURE Color.
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.
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 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' ) ).
#
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.
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.
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.
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' ).
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 valueFor 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.
NEW
Instances of classes can also be created with NEW
:
NEW <class name>( <constructor parameter> )
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.
my_method( TEXT = CONV #( sy-datum ) ).
DATA DateText TYPE char8.
DateText = sy-date.
my_method( Text = DateText )
See the enumerations for more examples.
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:
DATA Task TYPE zbc_tasks.
DATA(Components) = cast cl_abap_structdescr(
cl_abap_typedescr=>describe_by_data(
Task ) )->get_components( ).
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( ).
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
:
out->write(
|Hello { SWITCH #( User-Gender
WHEN 'F' THEN 'Mrs.'
WHEN 'M' THEN 'Mr.'
ELSE '' )
} { User-Lastname } | ).
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' ) }|.
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 = ', ' ) ).
FILTER
operatorThe 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
clauseDATA 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 ) ).
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.
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 ).
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 tableThis 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 ).
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
.
IF isRelevant( ).
...
ENDIF.
IF isRelevant( ) EQ abap_true.
...
ENDIF.
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.
* 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'.
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
.
* Changing Peter's last name
TRY.
users[ firstname = 'Peter'
]-lastname = 'Pan'.
CATCH CX_SY_ITAB_LINE_NOT_FOUND.
ENDTRY.
* 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 )
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 LOOP
s.
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.
LOOP AT PlantMats
INTO DATA(PlantMat).
AT NEW Plant.
" A
ENDAT.
" B
AT END OF Plant.
" C
ENDAT.
ENDLOOP.
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.