ABAP 快速入门之数据定义和动态编程

333 阅读6分钟

数据声明相关示例

内联数据声明

在某些情况下,数据声明可以内联执行。

LOOP AT lt_sflight INTO DATA(ls_sflight).
    WRITE ls_sflight-carrid.
ENDLOOP.

SELECT 语句中的内联数据声明

SELECT...ENDSELECT 块或 SELECT SINGLE 语句中使用内联数据声明时,必须使用 @ 字符作为 DATA(lv_cityto) 表达式的转义字符。一旦使用了转义字符,所有其他主机变量也必须转义(如下面的 lv_carrid)。

DATA lv_carrid TYPE s_carr_id VALUE 'LH'.
SELECT SINGLE cityto FROM spfli
       INTO @DATA(lv_cityto)
       WHERE carrid = @lv_carrid
       AND   connid = 2402.
WRITE: / lv_cityto.

多变量声明

DATA: begda TYPE sy-datum,
      endda TYPE sy-datum.

单变量声明

DATA begda TYPE sy-datum.

变量声明选项

可以使用特殊选项声明不同类型的变量。

DATA: lv_string   TYPE string, " standard declaration
      lv_char     TYPE c,      " declares a character variable of length 1
      lv_char5(5) TYPE c,      " declares a character variable of length 5
      l_packed TYPE p LENGTH 10 DECIMALS 5 VALUE '1234567890.123456789'. " evaluates to 1,234,567,890.12346 

动态编程

数据引用

对于数据引用来说,在 TYPE 后添加 REF TO 是必不可少的。

动态创建结构

如果结构的类型应在运行时决定,我们可以将目标结构定义为对通用类型数据的引用 data

DATA wa TYPE REF TO data.

我们使用语句 CREATE DATAwa 添加类型。添加的 TYPE 可以通过以下方式指定:

引用:

CREATE DATA wa TYPE kna1
  • 静态检查是有效的,因此不可能创建未知类型

命名:

CREATE DATA wa TYPE (lw_name_as_string)
  • 需要使用括号,lw_name_as_string 包含字符串形式的类型名称。
  • 如果未找到类型,将产生 CX_SY_CREATE_DATA_ERROR 类型的异常

对于动态创建类型的实例化,可以指定 HANDLE 附加。HANDLE 接收一个继承自 CL_ABAP_DATADESCR 的对象。

CREATE DATA dref TYPE HANDLE obj
  • 可使用运行时类型服务( RunTime Type Services)创建 obj
  • 由于 dref 仍然是一个数据引用,因此必须对其进行取消引用 (->*) 才能将其用作数据容器(通常通过字段符号 Field-Symbols 完成)。

Field-Symbols

字段符号(Field-Symbols)相当于 ABAP 的指针,只是字段符号总是被取消引用(不可能更改内存中的实际地址)。

定义

要声明字段符号,必须使用关键字 FIELD-SYMBOLS。类型可以是通用类型(ANY [...... TABLE]),以处理各种变量。

FIELD-SYMBOLS: <fs_line>     TYPE any,    "generic
               <fs_struct>   TYPE kna1.   "non-generic

Assigning

字段符号在声明时是未赋值的,这意味着它们什么也不指向。访问未赋值的字段符号会导致异常,如果未捕获,还会导致短转储。因此,应使用 IS ASSIGNED 检查状态:

IF <fs> IS ASSIGNED.
*... symbol is assigned
ENDIF.

由于它们只是引用,内部无法存储真实数据。因此,每次使用时都需要声明 DATA

DATA: w_name  TYPE string VALUE `Max`,
      w_index TYPE i      VALUE 1.
 
FIELD-SYMBOLS <fs_name> TYPE any.

ASSIGN w_name TO <fs_name>. "<fs_name> now gets w_name
<fs_name> = 'Manni'.        "Changes to <fs_name> now also affect w_name

* As <fs_name> is generic, it can also be used for numbers

ASSIGN w_index TO <fs_name>. "<fs_name> now refers to w_index.    
ADD 1 TO <fs_name>.          "w_index gets incremented by one

Unassigning

有时,重置字段符号可能很有用。这可以使用 UNASSIGN(取消指定)来实现。

UNASSIGN <fs>.
* Access on <fs> now leads to an exception again

用于内表

字段符号可用于修改内部表格。

LOOP AT itab INTO DATA(wa).
* Only modifies wa_line
    wa-name1 = 'Max'. 
ENDLOOP.    

LOOP AT itab ASSIGNING FIELD-SYMBOL(<fs>).
* Directly refers to a line of itab and modifies its values
    <fs>-name1 = 'Max'. 
ENDLOOP.

注意!即使离开循环后,字段符号仍会保留。如果想安全地重复使用,请立即取消分配(unassign)。

运行时类型服务

官方文档:RTTS - Runtime Type Services - ABAP Keyword Documentation (sap.com)

运行时类型服务(RunTime Type Services,简称:RTTS),通过类型描述类的层次结构实现的,其中包含

  • 创建类型(RunTime Type Creation,运行时类型创建;简称:RTTC
  • 分析类型(RunTime Type Identification,运行时类型鉴定;简称:RTTI

使用这些系统类可以:

  • 运行时确定 ABAP 类型系统中现有实例和类型名称的类型信息
  • 运行时定义新的数据类型

概念

types 的属性由类型描述对象(type description objects)的属性表示。每个类型都有一个类型描述对象。类型描述对象的属性包含有关类型属性的信息。每一类类型(基本类型、表、类等)都有一个类型描述类,其特殊属性用于表示特殊类型的属性。类型描述类的类层次结构与 ABAP 类型系统中类型类别的层次结构相对应。

此外,复杂类型、引用、类和接口的类型描述类还具有指定部分类型引用的特殊方法。这些方法可用于使用复合类型导航到所有部分类型。

只有使用类型描述类的方法才能创建类型描述对象。要获取对某一类型的类型描述对象的引用,可以使用 CL_ABAP_TYPEDESCR 类的静态方法或调用特殊类型描述类的方法。

笔记: 在 CREATE DATA 语句中,可以在添加 HANDLE 之后指定类型描述对象,以动态创建数据类型来创建数据对象。

类型描述类的层次结构

CL_ABAP_TYPEDESCR 
  | 
  |--CL_ABAP_DATADESCR 
  |   | 
  |   |--CL_ABAP_ELEMDESCR 
  |   |--CL_ABAP_REFDESCR 
  |   |--CL_ABAP_COMPLEXDESCR 
  |       | 
  |       |--CL_ABAP_STRUCTDESCR 
  |       |--CL_ABAP_TABLEDESCR 
  | 
  |--CL_ABAP_OBJECTDESCR 
     | 
     |--CL_ABAP_CLASSDESCR 
     |--CL_ABAP_INTFDESCR

image.png

CL_ABAP_TYPEDESCR 是基类,提供了四种方法来派生类型的描述对象:

  • DESCRIBE_BY_DATA:该方法将数据作为输入,并返回数据类型描述对象的对象引用。
  • DESCRIBE_BY_NAME:该方法将类型名称作为输入参数,并返回相应描述对象的对象引用。
  • DESCRIBE_BY_OBJECT_REF:此方法将对象引用作为输入,并返回一个对象引用,指向引用所指向对象类型的描述对象。
  • DESCRIBE_BY_DATA_REF:此方法将数据引用作为输入,并返回一个对象引用,指向引用所指向数据类型的描述对象。

可以通过不同的方式(如通过名称、数据、导航等)获取类型的描述对象引用。这一点很重要--RTTI 保证每种类型都有一个描述对象。所有引用都指向同一个描述对象,无论选择哪种路径进行检索。

RTTI 示例

REPORT typedescr_test.

TYPES my_type TYPE i.

DATA: my_data   TYPE my_type,
      descr_ref TYPE ref to cl_abap_typedescr.

START-OF-SELECTION.
  descr_ref = cl_abap_typedescr=>describe_by_data( my_data ).

  WRITE: / 'type name:', descr_ref->absolute_name.
  WRITE: / 'kind    :', descr_ref->type_kind.
  WRITE: / 'length       :', descr_ref->length.
  WRITE: / 'Decimals:', descr_ref->decimals.

后续将专门用一篇文章进行介绍