본문 바로가기

SAP/Release 7.40

Inline Declarations

Inline declarations are a new way of declaring variables and field symbols at operand positions.

   

Data Declarations

   

In ABAP you have many operand positions, where the value of the operand is changed by the statement. The most typical of these "write positions" is the left hand side lhs of an assignment.

   

lhs = rhs.

   

But of course there are more. The data objects you can use at these write positions are either writable formal parameters of the procedure you are working in or variables declared with DATA in front of the statement.

   

In many cases the variables filled by a statement are helper variables that you only need close to the statement. For each of  these helper variables you had to write a data declaration with the DATA statement and of course it was your task to give the variable an adequate type.

   

Well, the operand type of most write positions is statically fixed and well known to the compiler. And this is why ABAP can offer inline data declarations with Release 7.40. The ingredients are so called declaration positions (write positions with fully known operand type)  and the new declaration operator DATA(...).

   

Let's look at some examples.

   

Declaration of a lhs-variable for a simple assignment

   

Before 7.40

   

DATA text TYPE string.

text = `...`.

   

With 7.40

   

DATA(text) = `...`.

   

   

Declaration of table work areas

   

Before 7.40

   

DATA wa like LINE OF itab.

LOOP AT itab INTO wa.   

  ...

ENDLOOP.

   

With 7.40

   

LOOP AT itab INTO DATA(wa).   

  ...

ENDLOOP.

   

   

Declaration of a helper variable

   

Before 7.40

   

DATA cnt TYPE i.

FIND ... IN ... MATCH COUNT cnt.

   

With 7.40

   

FIND ... IN ... MATCH COUNT DATA(cnt).

   

   

Declaration of a result

   

Before 7.40

   

DATA xml TYPE xstring.

CALL TRANSFORMATION ... RESULT XML xml.

   

With 7.40

   

CALL TRANSFORMATION ... RESULT XML DATA(xml).

   

   

Declaration of actual parameters

   

Before 7.40

   

DATA a1 TYPE ...

DATA a2 TYPE ...

oref->meth( IMPORTING p1 = a1

            IMPORTING p2 = a2

            ... )

   

With 7.40

   

oref->meth( IMPORTING p1 = DATA(a1)

            IMPORTING p2 = DATA(a2)

            ... ).

   

   

Declaration of reference variables for factory methods

   

Before 7.40

   

DATA ixml           TYPE REF TO if_ixml.

DATA stream_factory TYPE REF TO if_ixml_stream_factory.

DATA document       TYPE REF TO if_ixml_document.

   

ixml           = cl_ixml=>create( ).

stream_factory = ixml->create_stream_factory( ).

document       = ixml->create_document( ).

   

With 7.40

   

DATA(ixml)           = cl_ixml=>create( ).

DATA(stream_factory) = ixml->create_stream_factory( ).

DATA(document)       = ixml->create_document( ).

   

This example is my favorite. When working with class libraries as the iXML-Library you don't have to care about the data type of the reference variables too much any more. You simply create them inline and use them. As you will see in the 7.40 version of the ABAP Example Library, this feature has facilitated my writings of example programs considerably.

   

   

Field Symbols

   

For field symbols there is the new declaration operator FIELD-SYMBOL(...) that you can use at exactly three declaration positions.

   

ASSIGN ... TO FIELD-SYMBOL(<fs>).

   

LOOP AT itab ASSIGNING FIELD-SYMBOL(<line>).

...

ENDLOOP.

   

READ TABLE itab ASSIGNING FIELD-SYMBOL(<line>) ...

   

I guess it is clear to you what happens here.

   

Outlook

   

In my upcoming blogs I will make use of inline declarations when introducing other new features. Be prepared for code like this:

   

TYPES t_itab TYPE TABLE OF i WITH EMPTY KEY.

   

DATA(itab) = VALUE t_itab( ( 1 ) ( 2 ) ( 3 ) ).

   

Yes, this is ABAP 7.40 ...

   

원본 위치 <http://scn.sap.com/community/abap/blog/2013/05/23/abap-news-for-release-740--inline-declarations>

   

   

   

Constructor Operator NEW

2014년 7월 9일 수요일

오전 10:51

   

With Release 7.40 ABAP supports so called constructor operators. Constructor operators are used in constructor expressions to create a result that can be used at operand positions. The syntax for constructor expressions is

   

... operator type( ... ) ...

   

operator is a constructor operator. type is either the explicit name of a data type or the character #. With # the data type can be dreived from the operand position if the operand type is statically known. Inside the parentheses specific parameters can be specified.

   

   

Instantiation Operator NEW

   

The instantiation operator NEW is a constructor operator that creates an object (anonymous data object or instance of a class).

   

  • ... NEW dtype( value ) ...

       

    creates an anonymous data object of data type dtype and passes a value to the created object. The value construction capabilities cover structures and internal tables (same as those of the VALUE operator).

       

  • ... NEW class( p1 = a1 p2 = a2 ... ) ...

       

    creates an instance of class class and passes parameters to the instance constructor.

       

  • ... NEW #( ... ) ...

       

    creates either an anonymous data object or an instance of a class depending on the operand type.

   

You can write a compnent selector -> directly behind NEW type( ... ).

   

Example for data objects

   

Before Release 7.40

   

FIELD-SYMBOLS <fS> TYPE data.

DATA dref TYPE REF TO data.

CREATE DATA dref TYPE i.

ASSIGN dref->* TO <fs>.

<fs> = 555.

   

With Release 7.40

   

DATA dref TYPE REF TO data.

dref = NEW i( 555 ).

   

Example for instances of classes

   

Before Release 7.40

   

DATA oref TYPE REF TO class.

CREATE OBJECT oref EXPORTING ...

   

With Release 7.40

   

Either

   

DATA oref TYPE REF TO class.

oref = NEW #( ... ).

   

or with an inline declaration

   

DATA(oref) = NEW class( ... ).

   

This is the kind of statement NEW is made for. You can also pass it to methods expecting references.

   

And one for the road ...

   

   

TYPES: BEGIN OF t_struct1, 

         col1 TYPE i, 

         col2 TYPE i, 

       END OF t_struct1, 

       BEGIN OF t_struct2, 

         col1 TYPE i, 

         col2 TYPE t_struct1, 

         col3 TYPE TABLE OF t_struct1 WITH EMPTY KEY, 

       END OF t_struct2, 

       t_itab TYPE TABLE OF t_struct2 WITH EMPTY KEY. 

   

DATA(dref) = 

  NEW t_itab( ( col1 = 1 

                col2-col1 = 1 

                col2-col2 = 2 

                col3 = VALUE #( ( col1 = 1 col2 = 2 ) 

                                ( col1 = 3 col2 = 4 ) ) ) 

              ( col1 = 2 

                col2-col1 = 2 

                col2-col2 = 4 

                col3 = VALUE #( ( col1 = 2 col2 = 4 ) 

                                ( col1 = 6 col2 = 8 ) ) ) ). 

   

원본 위치 <http://scn.sap.com/community/abap/blog/2013/05/24/abap-news-for-release-740--constructor-operator-new>

   

   

   

Value operator VALUE

2014년 7월 9일 수요일

오전 10:52

   

With Release 7.40 ABAP supports so called constructor operators. Constructor operators are used in constructor expressions to create a result that can be used at operand positions. The syntax for constructor expressions is

   

... operator type( ... ) ...

   

operator is a constructor operator. type is either the explicit name of a data type or the character #. With # the data type can be dreived from the operand position if the operand type is statically known. Inside the parentheses specific parameters can be specified.

   

   

Value Operator VALUE

   

The value operator VALUE is a constructor operator that constructs a value for the type specified with type.

   

  • ... VALUE dtype|#( ) ...

          

    constructs an initial value for any data type.

       

  • ... VALUE dtype|#( comp1 = a1 comp2 = a2 ... ) ...

       

    constructs a structure where for each component a value can be assigned.

       

  • ... VALUE dtype|#( ( ... ) ( ... ) ... ) ...

   

constructs an internal table, where for each line a value can be assigned. Inside inner parentheses you can use the syntax for structures but not the syntax for table lines directly. But you can nest VALUE operators.

   

Note that you cannot construct elementary values (which is possible with instantiation operator NEW) - simply because there is no need for it.

   

For internal tables with a structured line type there is a short form that allows you to fill columns with the same value in subsequent lines

   

   

   

   

VALUE dtype|#( col1 = dobj11 ... ( col2 = dobj12 col3 = dobj13 ... ) 

                                 ( col2 = dobj22 col3 = dobj23 ... ) 

                                   ... 

               col1 = dobj31 col2 = dobj32 ... ( col3 = dobj33 ... ) 

                                               ( col3 = dobj43 ... ) 

               ... ).

   

Example for initial values

   

Why would you like to construct an initial value anyhow? Well, you can pass an initial actual parameter to a structured or tabular formal parameter without the need of an initial hleper variable now.

   

CLASS c1 DEFINITION. 

  PUBLIC SECTION. 

    TYPES: BEGIN OF t_struct, 

             col1 TYPE i, 

             col2 TYPE i, 

           END OF t_struct. 

    CLASS-METHODS m1 IMPORTING p TYPE t_struct. 

ENDCLASS. 

   

CLASS c1 IMPLEMENTATION. 

  METHOD m1. 

    ... 

  ENDMETHOD. 

ENDCLASS. 

   

START-OF-SELECTION. 

   

  c1=>m1( VALUE #( ) ).

   

Example for structures

   

Three different ways to construct the same nested structure:

   

TYPES:  BEGIN OF t_col2, 

           col1 TYPE i, 

           col2 TYPE i, 

        END OF t_col2. 

   

TYPES: BEGIN OF t_struct, 

         col1 TYPE i, 

         col2 TYPE t_col2, 

       END OF t_struct. 

   

DATA: struct TYPE t_struct, 

      col2 TYPE t_col2. 

"1

struct = VALUE t_struct( col1 = 1 

                         col2-col1 = 1 

                         col2-col2 = 2 ). 

   

"2

col2   = VALUE   t_col2( col1 = 1 

                         col2 = 2 ). 

struct = VALUE t_struct( col1 = 1 

                         col2 = col2 ). 

   

"3

struct = VALUE t_struct( col1 = 1 

                         col2 = VALUE #( col1 = 1 

                                         col2 = 2 ) ).

   

Examples for internal tables

   

Elementary line type:

   

TYPES t_itab TYPE TABLE OF i WITH EMPTY KEY. 

   

DATA itab TYPE t_itab. 

   

itab = VALUE #( ( ) ( 1 ) ( 2 ) ).

   

Structured line type (RANGES table):

   

DATA itab TYPE RANGE OF i. 

   

itab = VALUE #( sign = 'I'  option = 'BT' ( low = 1  high = 10 ) 

                                          ( low = 21 high = 30 ) 

                                          ( low = 41 high = 50 ) 

                            option = 'GE' ( low = 61 )  ).

   

Other expressions in VALUE operator

   

Of course, the arguments of VALUE can be expressions or function calls:

   

TYPES t_date_tab TYPE TABLE OF string  WITH EMPTY KEY.

   

DATA(date_tab) = VALUE t_date_tab( 

  ( |{ CONV d( sy-datlo - 1 ) DATE = ENVIRONMENT }| ) 

  ( |{         sy-datlo       DATE = ENVIRONMENT }| ) 

  ( |{ CONV d( sy-datlo + 1 ) DATE = ENVIRONMENT }| ) ).

   

   

So you can do a lot of crazy things now, but be aware of obfuscation ...

   

원본 위치 <http://scn.sap.com/community/abap/blog/2013/05/27/abap-news-for-release-740--constructor-operator-value>

   

   

Reference operator REF

2014년 7월 9일 수요일

오전 10:52

   

With Release 7.40 ABAP supports so called constructor operators. Constructor operators are used in constructor expressions to create a result that can be used at operand positions. The syntax for constructor expressions is

   

... operator type( ... ) ...

   

operator is a constructor operator. type is either the explicit name of a data type or the character #. With # the data type can be dreived from the operand position if the operand type is statically known. Inside the parentheses specific parameters can be specified.

   

   

Reference Operator REF

   

This is a short one but can become handy for some purposes.

   

The reference operator REF constructs a data reference at operand positions.

   

... REF dtype|#( dobj ) ...

   

results in a data reference pointing to dobj with the static type specified by type. With other words, REF is the short form for GET REFERENCE OF dobj INTO.

   

Where do you need it? Always when you have to pass data references to somewhere and don't want to create helper variables for that purpose.

   

Example for dynamic method call

   

Using REF for filling the parameter table:

   

CLASS class DEFINITION.

  PUBLIC SECTION.

    METHODS meth

      IMPORTING p1 TYPE string

                p2 TYPE i.

ENDCLASS.

   

CLASS class IMPLEMENTATION.

  METHOD meth.

    ...

  ENDMETHOD.

ENDCLASS.

   

START-OF-SELECTION.

   

  DATA(arg1) = `blah`.

  DATA(arg2) = 111.

   

  DATA(ptab) = VALUE abap_parmbind_tab(

    ( name = 'P1' kind = cl_abap_objectdescr=>exporting value = REF #( arg1 ) )

    ( name = 'P2' kind = cl_abap_objectdescr=>exporting value = REF #( arg2 ) ) ).

   

  DATA(oref) = NEW class( ).

  CALL METHOD oref->('METH')

    PARAMETER-TABLE ptab.

   

Example for ADBC

   

Parameter binding made easy!

   

DATA(key) = 'LH'.

   

DATA(sql) = NEW cl_sql_statement( ).

sql->set_param( REF #( sy-mandt ) ).

sql->set_param( REF #( key ) ).

   

DATA(result) = sql->execute_query(

      `SELECT carrname ` &&

      `FROM scarr ` &&

      `WHERE mandt  = ? AND carrid = ?` ).

   

DATA name TYPE scarr-carrname.

result->set_param( REF #( name ) ).

result->next( ).

   

Believe it or not: name contains the carrier name now.

   

원본 위치 <http://scn.sap.com/community/abap/blog/2013/05/27/abap-news-for-release-740--constructor-operator-ref>

   

   

   

Conversion and casting operators CONV and CAST

2014년 7월 9일 수요일

오전 10:52

   

With Release 7.40 ABAP supports so called constructor operators. Constructor operators are used in constructor expressions to create a result that can be used at operand positions. The syntax for constructor expressions is

   

... operator type( ... ) ...

   

operator is a constructor operator. type is either the explicit name of a data type or the character #. With # the data type can be dreived from the operand position if the operand type is statically known. Inside the parentheses specific parameters can be specified.

   

   

Conversion Operator CONV

   

The conversion operator CONV is a constructor operator that converts a value into the type specified in type.

   

... CONV dtype|#( ... ) ...

   

You use CONV  where you needed helper variables before in order to achieve a requested data type.

   

Example for parameter passing

   

Method cl_abap_codepage=>convert_to expects a string but you want to convert a text field.

   

Before release 7.40

   

DATA text TYPE c LENGTH 255.

   

DATA helper TYPE string.

DATA xstr   TYPE xstring.

helper = text.

xstr = cl_abap_codepage=>convert_to( source = helper ).

   

   

With release 7.40

   

DATA text TYPE c LENGTH 255.

DATA(xstr) = cl_abap_codepage=>convert_to( source = CONV string( text ) ).

   

In such cases it is even simpler to write

   

DATA text TYPE c LENGTH 255.

DATA(xstr) = cl_abap_codepage=>convert_to( source = CONV #( text ) ).

   

Example for influencing a calculation

   

IF 1 / 3 > 0.

  ...

ENDIF. 

   

is false, but

   

IF CONV decfloat34( 1 / 3 ) > 0.

  ...

ENDIF.

   

is true!

   

   

Example for influencing a comparison

   

The infamous

   

IF ' ' = ` `.

  ...

ENDIF.

   

is false. But

   

IF ' ' = CONV char1( ` ` ).

  ...

ENDIF.

   

is true!

   

   

Casting Operator CAST

   

The casting operator CAST is a constructor operator that executes an up or down cast for reference varaibles with the type specified in type.

   

... CAST dtype|class|interface|#( ... ) ...

   

  • You use CAST for a down cast where you needed helper variables before in order to cast with ?= to a requested reference type.
  • You use CAST for an up cast, e,g, with an inline declaration, in order to construct a more general type.

       

    You can write a compnent selector -> directly behind CAST type( ... ).

       

    Example from RTTI

       

    Common example where a down cast is needed.

       

    Before release 7.40

       

    DATA structdescr TYPE REF TO cl_abap_structdescr.

    structdescr ?= cl_abap_typedescr=>describe_by_name( 'T100' ).

       

    DATA components  TYPE abap_compdescr_tab.

    components = structdescr->components.

       

    With release 7.40

       

    DATA(components) = CAST cl_abap_structdescr(

      cl_abap_typedescr=>describe_by_name( 'T100' ) )->components.

       

       

    Example with up cast

       

    The static type of the reference variable iref declared inline should be the interface not the class.

       

    INTERFACE if. 

      ... 

    ENDINTERFACE. 

       

    CLASS cl DEFINITION CREATE PRIVATE. 

      PUBLIC SECTION. 

        INTERFACES if. 

        CLASS-METHODS factory RETURNING value(ref) TYPE REF TO cl. 

        ... 

    ENDCLASS. 

       

    CLASS cl IMPLEMENTATION. 

      METHOD factory. 

        ref = NEW #( ). 

      ENDMETHOD. 

    ENDCLASS. 

       

    START-OF-SELECTION. 

      DATA(iref) = CAST if( cl=>factory( ) ).

       

    Example with data objects

       

    A constructor expression with CAST followed by -> is an LHS-expression, you can assign values to it.

       

       

    TYPES: BEGIN OF t_struc

            col1 TYPE i, 

            col2 TYPE i, 

           END OF t_struc

       

    DATA dref  TYPE REF TO data. 

    DATA struc TYPE t_struc

       

    dref = NEW t_struc( ). 

       

    CAST t_struc( dref )->col1 struc-col1

       

    원본 위치 <http://scn.sap.com/community/abap/blog/2013/05/27/abap-news-for-release-740--constructor-operators-conv-and-cast>

       

   

   

Lossless operator EXACT

2014년 7월 9일 수요일

오전 10:52

   

With Release 7.40 ABAP supports so called constructor operators. Constructor operators are used in constructor expressions to create a result that can be used at operand positions. The syntax for constructor expressions is

   

... operator type( ... ) ...

   

operator is a constructor operator. type is either the explicit name of a data type or the character #. With # the data type can be dreived from the operand position if the operand type is statically known. Inside the parentheses specific parameters can be specified.

   

   

Lossless Operator EXACT

   

The lossless operator EXACT is a constructor operator that exceutes either a lossless calculation or a lossless assignment.

   

  • ... EXACT dtype|#( arith_exp ) ... 
    arith_exp is an arithmetic expression that is calculated lossless with calculation type decfloat34 and the result is converted to the specified type.
  • ... EXACT dtype|#( arg ) ... 
    arg is not an arithmetic expression and its value is assigned to the result of the specified type according to the rules for lossless assignments.

   

Lossless calculations

   

Lossless calculations were introduced for decimal floating point numbers in Release 7.02 with the addition EXACT toCOMPUTE. This addition (better the whole statement COMPUTE) became obsolete now.

   

A lossless calculation must not perform any roundings. If it does, an exception occurrs.

   

Example

   

TRY.

    DATA(r1) = EXACT decfloat34( 3 / 2 ).

    cl_demo_output=>write( |Exact: { r1 }| ).

  CATCH cx_sy_conversion_rounding INTO DATA(e1).

    cl_demo_output=>write( |Not exact: { e1->value }| ).

ENDTRY.

   

TRY.

    DATA(r2) = EXACT decfloat34( 3 / 7 ).

    cl_demo_output=>write( |Exact: { r2 }| ).

  CATCH cx_sy_conversion_rounding INTO DATA(e2).

    cl_demo_output=>write( |Not exact: { e2->value }| ).

ENDTRY.

   

cl_demo_output=>display( ).

   

The output is:

   

Exact: 1.5

   

   

Not exact: 0.4285714285714285714285714285714286

   

You see that the non-exact result can be found in th exception object.

   

   

Lossless assignments

   

Lossless assignments were introduced for conversions in Release 7.02 with the addition EXACT to MOVE. This addition (better the whole statement MOVE) became obsolete now.

   

A lossless assignment is an assignment where

   

  • the value of the source is valid for its type
  • there are no data losses during the assignment
  • the converted value of the target is valid for its type

   

Example

   

   

TYPES numtext TYPE n LENGTH 10.

   

cl_demo_output=>write( CONV numtext( '4 Apples + 2 Oranges' ) ).

   

TRY.

    DATA(number) = EXACT numtext( '4 Apples + 2 Oranges' ).

  CATCH cx_sy_conversion_error INTO DATA(exc).

  cl_demo_output=>write( exc->get_text( ) ).

ENDTRY.

   

cl_demo_output=>display( ).

   

The result is

   

0000000042

   

The argument '4 Apples + 2 Oranges' cannot be interpreted as a number

   

The infamous conversion rule from c to n is not supported by EXACT.

   

원본 위치 <http://scn.sap.com/community/abap/blog/2013/05/28/abap-news-for-release-740--constructor-operator-exact>

   

   

   

Conditional operators COND and SWITCH

2014년 7월 9일 수요일

오전 10:52

   

With Release 7.40 ABAP supports so called constructor operators. Constructor operators are used in constructor expressions to create a result that can be used at operand positions. The syntax for constructor expressions is

   

... operator type( ... ) ...

   

operator is a constructor operator. type is either the explicit name of a data type or the character #. With # the data type can be dreived from the operand position if the operand type is statically known. Inside the parentheses specific parameters can be specified.

   

   

Conditional operators COND and SWITCH

   

Last but not least, two nice ones, the conditionals COND and SWITCH.

   

  • ... COND dtype|#( WHEN log_exp1 THEN result1 
                    [ WHEN log_exp2 THEN result2 ] 
                    ... 
                    [ ELSE resultn ] ) ...

           constructs a result of the specified type that depends on logical expressions.

  • ... SWITCH dtype|#( operand 
                        WHEN const1 THEN result1 
                      [ WHEN const2 THEN result2 ] 
                      ... 
                      [ ELSE resultn ] ) ...

    constructs a result of the specified type that depends on a case differentiation.

       

    With other words: IF and CASE as expressions in operand positions!

               

    Example for COND

       

    DATA(time) =

      COND string(

        WHEN sy-timlo < '120000' THEN

          |{ sy-timlo TIME = ISO } AM|

        WHEN sy-timlo > '120000' THEN

          |{ CONV t( sy-timlo - 12 * 3600 )

             TIME = ISO } PM|

        WHEN sy-timlo = '120000' THEN

          |High Noon|

        ELSE

          THROW cx_cant_be( ) ).

       

    Note the THROW. Now you can raise and  throw exceptions ...

       

    Example for SWITCH

       

    CLASS cx_langu_not_supported DEFINITION INHERITING FROM cx_static_check.

    ENDCLASS.

       

    CLASS class DEFINITION.

      PUBLIC SECTION.

        METHODS meth IMPORTING iso_langu   TYPE string

                     RETURNING VALUE(text) TYPE string.

    ENDCLASS.

       

    CLASS class IMPLEMENTATION.

      METHOD meth.

        ...

      ENDMETHOD.

    ENDCLASS.

       

    ...

       

       

      DATA(text) =

        NEW class(

          )->meth(

              SWITCH #( sy-langu

                        WHEN 'D' THEN `DE`

                        WHEN 'E' THEN `EN`

                        ELSE THROW cx_langu_not_supported( ) ) ).

       

    Amazing, n'est-ce pas?

       

    원본 위치 <http://scn.sap.com/community/abap/blog/2013/05/28/abap-news-for-release-740--constructor-operators-cond-and-switch>

       

   

   

ABAP Objects

2014년 7월 9일 수요일

오전 10:56

   

For ABAP Objects itself - meaning the statements for dealing with classes and objects in ABAP - there are two small but nice improvements in Release 7.40.

   

   

Parameter Interface of Functional Methods

   

Before Release 7.40 a functional method could have only importing parameters besides its returning parameter.

   

A functional method can now have exporting and changing parameters besides its returning parameter.

   

In functional method calls you can use the additions EXPORTINGIMPORTING, and CHANGING to pass parameters.

   

Example

   

The following kind of functional method declaration and invocation was not possible before Release 7.40.

   

CLASS class DEFINITION. 

  PUBLIC SECTION. 

    CLASS-METHODS do_something IMPORTING p1 TYPE i 

                                         p2 TYPE i 

                               EXPORTING p3 TYPE i 

                                         p4 TYPE i 

                               RETURNING VALUE(r) TYPE i. 

ENDCLASS.

   

...

   

IF  class=>do_something( EXPORTING p1 = 333

                                   p2 = 444

                         IMPORTING p3 = a1

                                   p4 = a2 ) = 0.

  "work with a1, a2

ENDIF.

   

   

Interfaces in Test Classes

   

You know that you have to implement all the methods of each interface that you include in your classes with theINTERFACES statement.While this is OK for normal classes, since the user expects the interfaces to be implemented, it can be become rather tedious in test classes of ABAP Unit. Especially when creating test doubles by implementing interfaces in test classes it is cumbersome to implement (sometimes many, many ) empty interface methods that are not needed for testing. And since empty method implementations are reported by the extended program check, you even have to add a pragma ##needed to each of those!

   

Fortunately, there is now a new addition PARTIALLY IMPLEMENTED to the statement INTERFACES that can be used in test classes and makes an end to these problems (don't tell me that you don't have such problems since you are not testing ...).

   

Example

   

The following example is an excerpt from an test include of a http-handler class. Since a http-handler normally works with objects like request or response of the ICF-framework, for standalone testing you have to create appropriate test doubles (mock framework) by implementing the according interfaces. In the case shown here, a class for a mock request is created by implementing if_http_request. Before Release 7.40 this was rather awkward, since you had to implement all of the about 50 interface methods! In Release 7.40 you have to implement only those that are needed for testing.

   

Before Release 7.40

   

CLASS mock_request DEFINITION FOR TESTING FINAL. 

  PUBLIC SECTION. 

    INTERFACES if_http_request. 

ENDCLASS.

   

CLASS mock_request IMPLEMENTATION. 

  METHOD if_http_request~get_form_field. 

    value = SWITCH spfli-carrid( name WHEN 'carrid' THEN 'LH' 

                                      ELSE space ). 

  ENDMETHOD.

   

  METHOD if_http_entity~set_cdata.     ENDMETHOD.     "##needed

  METHOD if_http_entity~set_data.      ENDMETHOD.     "##needed

  METHOD if_http_entity~add_multipart. ENDMETHOD.     "##needed

  METHOD if_http_entity~append_cdate.  ENDMETHOD.     "##needed

  ...

   

   

ENDCLASS.

   

   

Release 7.40

   

CLASS mock_request DEFINITION FOR TESTING FINAL. 

  PUBLIC SECTION. 

    INTERFACES if_http_request PARTIALLY IMPLEMENTED

ENDCLASS.

   

CLASS mock_request IMPLEMENTATION. 

  METHOD if_http_request~get_form_field. 

    value = SWITCH spfli-carrid( name WHEN 'carrid' THEN 'LH' 

                                      ELSE space ). 

  ENDMETHOD. 

ENDCLASS.

   

원본 위치 <http://scn.sap.com/community/abap/blog/2013/06/20/abap-news-for-release-740--abap-objects>

   

   

   

Table expressions

2014년 7월 9일 수요일

오전 10:56

   

Table expressions with the syntax

   

... itab[ ... ] ...

   

are a new way for accessing table lines in operand positions. You can view a table expression simply as a short form of a READ TABLE statement. The result of a table expression is a single table line. All you have to know is the syntax that does the same as a given READ TABLE statement.

   

If a table line is not found, the exception CX_SY_ITAB_LINE_NOT_FOUND is raised. No sy-subrc from expressions, of course.

   

The operand positions where table expressions can be used are read positions but also some write positions where you can modify the resulting table line. Table expressions are LHS-expressions!

   

   

Specifiying the line

   

Index access using the primary index

   

The assignment of the table expression

   

wa = itab[ idx ].

   

does the same as

   

READ TABLE itab INDEX idx INTO wa.

   

Index access using a secondary index

   

The assignment of the table expression

   

wa = itab[ KEY key INDEX idx ].

   

does the same as

   

READ TABLE itab INDEX idx USING KEY key INTO wa.

   

Access using a free key

   

The assignment of the table expression

   

wa = itab[ col1 = ... col2 = ... ].

   

does the same as

   

READ TABLE itab WITH KEY col1 = ... col2 = ...  INTO wa.

   

Key access using a table key

   

The assignment of the table expressions

   

wa = itab[ KEY key col1 = ... col2 = ... ].

wa = itab[ KEY key COMPONENTS col1 = ... col2 = ... ].

   

do the same as

   

READ TABLE itab WITH TABLE KEY key COMPONENTS col1 = ... col2 = ...  INTO wa.

   

   

Influencing the result

   

With READ TABLE you can read into a work are, assign a field symbol or set a reference. The result of table expressions can be influenced accordingly.

   

  • ... itab[ ... ] ...
    as a rule works like READ TABLE ... ASSIGNING ... . The temporary result of the expression is a field symbol.

       

  • ... VALUE type( itab[ ... ] ) ...
    forces the expression to work like READ TABLE ... INTO ... . The temporary result of the expression is a data object.

       

  • ... REF type( itab[ ... ] ) ...
    forces the expression to work like READ TABLE ... REFERENCE INTO ... . The temporary result of the expression is a reference variable.

       

    While the third case is important, if you want to work with references to table lines, the results of the other two cases are normally transparent to the user. You don't have to care how the intermediate result is represented internally. But there are some performance considerations. The same rules when to use what hold for table expressions as forREAD TABLE. Therfeore, the syntax checker might kindly remind you from time to time to place the VALUE operator in front of a table expression (or to leave it away ...).

       

    Chainings

       

    The following chainings with table expressions are possible:

       

  • ... itab[ ...]-comp
  • ... struct-comp[ ... ] ...
  • ... itab[ ... ][ ... ] ...

    and combinations of those

       

    Fun example

       

    TYPES:

      BEGIN OF struc1,

        col1 TYPE i,

        col2 TYPE i,

      END OF struc1,

      itab1 TYPE TABLE OF struc1 WITH EMPTY KEY,

      itab2 TYPE TABLE OF itab1 WITH EMPTY KEY,

      BEGIN OF struc2,

        col1 TYPE i,

        col2 TYPE itab2,

      END OF struc2,

      itab3 TYPE TABLE OF struc2 WITH EMPTY KEY.

       

    DATA(itab) = VALUE itab3(

       ( col1 = 1  col2 = VALUE itab2(

                           ( VALUE itab1(

                               ( col1 = 2 col2 = 3 )

                               ( col1 = 4 col2 = 5 ) ) )

                           ( VALUE itab1(

                               ( col1 = 6 col2 = 7 )

                               ( col1 = 8 col2 = 9 ) ) ) ) )

       ( col1 = 10 col2 = VALUE itab2(

                           ( VALUE itab1(

                               ( col1 = 11 col2 = 12 )

                               ( col1 = 13 col2 = 14 ) ) )

                           ( VALUE itab1(

                               ( col1 = 15 col2 = 16 )

                               ( col1 = 17 col2 = 18 ) ) ) ) ) ).

       

    * Reading the column with value 13 with READ TABLE statements

       

    READ TABLE itab     INTO DATA(wa1) INDEX 2.

    READ TABLE wa1-col2 INTO DATA(wa2) INDEX 1.

    READ TABLE wa2      INTO DATA(wa3) INDEX 2.

    DATA(num1) = wa3-col1.

       

    * Reading the column with value 13 with chained table expressions

       

    DATA(num2) = itab[ 2 ]-col2[ 1 ][ 2 ]-col1.

       

    Unbelievable, is this still ABAP?

       

    원본 위치 <http://scn.sap.com/community/abap/blog/2013/05/29/abap-news-for-release-740--table-expressions>

       

   

   

Internal table functions

2014년 7월 9일 수요일

오전 10:56

   

The most important news for internal tables are the Table expressions that allow the same single line access as the READ TABLE statement. But soon after those were available, the question arised "What about the system fields?".

   

We all use the well known patterns

   

READ TABLE ... TRANSPORTING NO FIELDS.

IF sy-subrc = 0.

  ...

ENDIF.

   

READ TABLE ... TRANSPORTING NO FIELDS.

DATA(idx) = sy-tabix.

...

   

Since an expression should be free of side effects it especially should not influence any system field. So what about these patterns?

The answer comes in form of two new built-in functions.

   

Line existence

   

A new predicate function that returns true if a line exists:

   

IF line_exists( itab[ ... ] ).

...

ENDIF.

   

Where itab[ ... ] is a table expression that works here like READ TABLE ... TRANSPORTING NO FIELDS.

   

(Note that we have already some other predicate functions in ABAP: matches and the contains-variants in the context of string processing).

   

Line index

   

A new table function that returns the number of the line in the index used (primary or secondary index):

   

DATA(idx)= line_index( itab[ ... ] ).

   

Where itab[ ... ] is a table expression that works here like READ TABLE ... TRANSPORTING NO FIELDS. If no line is found, the value 0 is returned. For Hash-Tables or hashed secondary keys, always the value -1 is returned.

   

(Note that this is the second table function in ABAP. The other one is lines).

   

원본 위치 <http://scn.sap.com/community/abap/blog/2013/06/22/abap-news-for-release-740--new-internal-table-functions>

   

   

   

Internal tables with empty key

2014년 7월 9일 수요일

오전 10:57

   

Internal tables with empty key?

   

Each internal table has a primary key. Even standard tables have a table key. Key access is not optimized for standard tables but the primary key plays a role if you use SORT itab without explicit sort fields or for table statements withFROM wa.

   

How do you declare a table? Pragmatically, often as follows:

   

DATA: BEGIN OF struct,

        col1 TYPE i,

        col2 TYPE i,

      END OF struct.

   

DATA itab LIKE TABLE OF struct.

   

Looks good. But now do the following (in all releases):

   

DATA: BEGIN OF struct,

        col1 TYPE i,

        col2 TYPE i,

      END OF struct.

   

DATA itab LIKE TABLE OF struct.

   

struct-col1 = 3. struct-col2 = 1.

APPEND struct TO itab.

struct-col1 = 2. struct-col2 = 2.

APPEND struct TO itab.

struct-col1 = 1. struct-col2 = 1.

APPEND struct TO itab.

   

DATA jtab LIKE itab.

jtab = itab.

   

SORT jtab.

   

IF jtab = itab.

  MESSAGE 'nop!' TYPE 'I'.

ENDIF.

   

SORT itab does nothing (some people tend to open a ticket now)! Why? Because itab has an empty key!

   

When you declare a standard table data object without specifiying the primary key, the default key is taken. The default key consists of all character and byte like fields of the table structrure. If the structure contains only numeric fields, duh! The same would have happened if you declared the DEFAULT KEY explicitly. But note that an empty key is not possible for sorted and hashed tables.

   

If you declare standard table types, the situation can become even more confusing.

   

TYPES: BEGIN OF struct,

         col1 TYPE i,

         col2 TYPE i,

       END OF struct.

   

TYPES itab TYPE STANDARD TABLE OF struct.

   

Here it is not the default key that is declared implicitly, but the table type is generic regarding its key.

   

To prove that, write

   

FIELD-SYMBOLS <itab> TYPE itab.

DATA jtab like <itab>.

   

Yo get a syntax error because the field symbol is generic and cannot be used behind LIKE.To get rid of the error you could add WITH DEFAULT KEY to the TYPES itab statement - and have an empty key again!

   

So, if you want to work with a table key it is always a good idea to explicitly define the key fields. Using the default key - either by chance or explicitly - is almost always critical because:

   

  • Identifying the key fields only by their data type often leads to unexpected behavior in sorts and other access types.
  • The possibility of an empty standard key in standard tables can cause unexpected behavior.
  • The standard key can contain many key fields, leading to performance problems.
  • In sorted tables and hashed tables the key fields are read-only, which can cause unexpected runtime errors.

       

    But what if you do not care about the key at all? If you simply want to use an internal table as an array that does not depend on key values?

       

    Before release 7.40, some unexpected behavior could arise for such arrays.With release 7.40 you can specify

       

    ... WITH EMPTY KEY.

       

    when declaring standard table objects or table types. If you don't care about the key, this new addition is always recommended in order to circumvent all problems with the default key or with a generic key.

       

    TYPES itab TYPE STANDARD TABLE OF string WITH EMPTY KEY.

       

    DATA(itab) = VALUE itab(

      ( `I'm going slightly mad` )

      ( `I'm going slightly mad` )

      ( `It finally happened - happened` )

      ( `It finally happened - ooh woh` ) ).

       

    Without explicit key declaration the type would not be usable for the inline data declaration shown here. Since I don't care about the key, I use the empty key. A SORT itab without specifying a sort key will do nothing and produce a warning from the syntax check.

       

    Starting with release 7.40 you declare your standard tables either with a good key or an empty key but never with the chancy default key!

       

    원본 위치 <http://scn.sap.com/community/abap/blog/2013/06/27/abap-news-for-release-740--internal-tables-with-empty-key>