ABAP News for Release 7.51 – Enumerations
It is a common pattern. A data object, say an attribute of a class, should assume only values from a given set. Before Release 7.51 that might have looked as follows:
CLASS cx_wrong_size DEFINITION INHERITING FROM cx_static_check. ENDCLASS. CLASS shirt DEFINITION. PUBLIC SECTION. TYPES tsize TYPE i. CONSTANTS: size_s TYPE tsize VALUE 0, size_m TYPE tsize VALUE 1, size_l TYPE tsize VALUE 2, size_xl TYPE tsize VALUE 3. METHODS constructor IMPORTING size TYPE tsize RAISING cx_wrong_size. ... PRIVATE SECTION. DATA size TYPE tsize. ENDCLASS. CLASS shirt IMPLEMENTATION. METHOD constructor. IF size <> size_s AND size <> size_m AND size <> size_l AND size <> size_xl. RAISE EXCEPTION TYPE cx_wrong_size. ENDIF. me->size = COND #( WHEN size <> size_s AND size <> size_m AND size <> size_l AND size <> size_xl THEN THROW cx_wrong_size( ) ELSE size ). ENDMETHOD. ENDCLASS.
Here, the size attribute of a shirt class should have only values that are defined as a set of constants in the class. Other values lead to an exception. A user can create an object of the shirt class as follows:
TRY. DATA(shirt) = NEW shirt( shirt=>size_xl ). CATCH cx_wrong_size. ... ENDTRY.
You see the overhead? Why not let the runtime environment do the value check for you? There is a concept for this called enumeration or enumerated type.
With Release 7.51, ABAP will support the concept of enumerations too. Let’s rewrite the example using such a new enumerated type:
CLASS shirt DEFINITION. PUBLIC SECTION. TYPES: BEGIN OF ENUM tsize, size_s, size_m, size_l, size_xl, END OF ENUM tsize. METHODS constructor IMPORTING size TYPE tsize. ... PRIVATE SECTION. DATA size TYPE tsize. ENDCLASS. CLASS shirt IMPLEMENTATION. METHOD constructor. me->size = size. ENDMETHOD. ENDCLASS.
A new variant of the TYPES statement, BEGIN OF ENUM – END OF ENUM, serves as a bracket around a set of constants. The standard base type of the constants is i and the enumerated values are counted up starting with 0.
The usage looks as before:
DATA(shirt) = NEW shirt( shirt=>size_xl ).
But hey, there’s no need for exception handling any more. If you try to pass an illegal value you get a syntax error!
DATA(shirt) = NEW shirt( 333 ).
Enumerations are a mixture of types and constants. With BEGIN OF ENUM enum – END OF ENUM enum you declare an elementary type enum that can be used behind the TYPE addition. In between, you declare a set of constants, the so called enumeration constants, that define the enumerated values that are allowed for enumerated objects that have the type enum. At this position TYPES in fact works as the CONSTANTS statement.
The ABAP runtime environment takes care, that only allowed enumerated values can be assigned to enumerated objects:
TYPES: BEGIN OF ENUM tsize, size_s, size_m, size_l, size_xl, END OF ENUM tsize. DATA size TYPE tsize. size = size_xl. "Allowed DATA dobj LIKE size. dobj = size. "Allowed dobj = 333. "Syntax or runtime error
You can assign enumerated objects only to enumerated objects of the same enumerated type and you can compare enumerated objects only with enumerated objects of the same enumerated type. Of course, this includes the enumeration constants itself.
Normally, you are not interested in the contents of an enumerated object at all. The semantics of an enumerated object is defined by the enumeration constant that defines its value. Nevertheless, you can define enumerated types with other base types than i and with other enumerated values (where one value must be initial):
TYPES: basetype TYPE c LENGTH 2, BEGIN OF ENUM tsize BASE TYPE basetype, size_i VALUE IS INITIAL, size_s VALUE `S`, size_m VALUE `M`, size_l VALUE `L`, size_xl VALUE `XL`, END OF ENUM tsize. DATA size TYPE tsize. size = size_xl. "Allowed DATA dobj LIKE size. dobj = size. "Allowed
This enables you to easily transform your existing “enumerated values” to the new concept. If you are lucky, you might be able to this without having to adjust their usage (in fact, I was able to do so for one of my classes).
If you have more than one enumerated type within one context, you might want to organize the respective enumerated values in structures:
TYPES: BEGIN OF ENUM tsize STRUCTURE size, s, m, l, xl, END OF ENUM tsize STRUCTURE size. DATA dobj TYPE tsize. dobj = size-xl. "Allowed
With that, you declare a constant enumeration structure size. The components of the structure are the enumeration constants of the enumerated type.
The main usage of enumerated objects is comparing them with the respective enumeration constants in order to branch to some according functionality.
TYPES: BEGIN OF ENUM tsize STRUCTURE size, s, m, l, xl, END OF ENUM tsize STRUCTURE size. DATA dobj TYPE tsize. ... CASE dobj. WHEN size-s. ... WHEN size-m. ... WHEN size-l. ... WHEN size-xl. ... ENDCASE.
Besides that, there are a few other allowed usages:
You can assign an enumerated object to text fields of type c or text strings of type string. The result is the name of the enumeration constant that defines the current value.
TYPES: BEGIN OF ENUM tsize, size_s, size_m, size_l, size_xl, END OF ENUM tsize. DATA text TYPE string. text = size_xl. cl_demo_output=>display( text ). "Output is SIZE_XL
You can also write
DATA(text) = CONV string( size_xl ).
You can access the current value with the CONV operator by specifying the base type.
TYPES: BEGIN OF ENUM tsize, size_s, size_m, size_l, size_xl, END OF ENUM tsize. DATA(value) = CONV i( size_xl ) . cl_demo_output=>display( value ). "Output is 3
The other way around, a valid enumerated value can be converted to an enumerated object (which is not possible in a normal assignment):
TYPES: BEGIN OF ENUM tsize, size_s, size_m, size_l, size_xl, END OF ENUM tsize. DATA(num) = 3. TRY. DATA(dobj) = CONV tsize( num ) . cl_demo_output=>display( dobj ). "Output is SIZE_XL CATCH cx_sy_conversion_no_enum_value. ... ENDTRY.
There is also a new class CL_ABAP_ENUMDESCR in RTTI.
TYPES: BEGIN OF ENUM tsize, size_s, size_m, size_l, size_xl, END OF ENUM tsize. DATA(size) = VALUE tsize( ). DATA(enum_descr) = CAST cl_abap_enumdescr( cl_abap_typedescr=>describe_by_data( size ) ). cl_demo_output=>new( )->write_data( enum_descr->kind "E, for elementary )->write_data( enum_descr->type_kind "k, new for enumerated type )->write_data( enum_descr->base_type_kind "I, the base type )->write_data( enum_descr->members "Table of constants and values )->display( ).
And that’s basically all.
Lean back and let the ABAP runtime environment do the work for you. It takes care that enumerated objects can only be used at appropriate operand positions and that they can only contain their prescribed values. An illegal enumerated value in an enumerated object should never occur (OK, there’s one known gap …).