运行时类型服务 练习

运行时类型服务 练习

#practice #rtts #rtti #rttc #dynamic-types #abap-objects

📌核心模式 (点击展开)
关键词 答案
RTTS Run Time Type Services,运行时类型服务总称
RTTI Run Time Type Identification,运行时类型识别
RTTC Run Time Type Creation,运行时类型创建
根类 CL_ABAP_TYPEDESCR
describe_by_data 根据数据对象获取类型描述
describe_by_name 根据类型名获取类型描述
describe_by_object_ref 根据对象引用获取类描述

题目1 - RTTS 概述与组成 [记忆]

什么是 RTTS?它由哪两个部分组成?各自的功能是什么?

📌查看答案
RTTS(Run Time Type Services) 是 ABAP 提供的运行时类型服务,允许程序在运行时检查和创建数据类型。

两个组成部分

组成 全称 功能
RTTI Run Time Type Identification 在运行时识别和检查数据类型信息
RTTC Run Time Type Creation 在运行时动态创建新的数据类型

核心类体系

1
2
3
4
5
6
7
8
9
CL_ABAP_TYPEDESCR            " 类型描述基类
├── CL_ABAP_ELEMDESCR " 基本类型(I, C, D, T, ...)
├── CL_ABAP_STRUCTDESCR " 结构体类型
├── CL_ABAP_TABLEDESCR " 内表类型
├── CL_ABAP_REFDESCR " 引用类型
├── CL_ABAP_CLASSDESCR " 类类型
├── CL_ABAP_INTFDESCR " 接口类型
├── CL_ABAP_DATADESCR " 数据描述基类
└── CL_ABAP_ENUMDESCR " 枚举类型

RTTI 用于读取类型信息(属性、组件等),RTTC 用于动态创建类型描述对象并由此创建数据引用。


题目2 - CL_ABAP_TYPEDESCR 的静态方法 [记忆]

CL_ABAP_TYPEDESCR 提供了哪些核心静态方法用于获取类型描述?请列出并说明各自的用途。

📌查看答案
CL_ABAP_TYPEDESCR 核心静态方法
方法 用途 示例
DESCRIBE_BY_DATA( ) 根据数据对象获取类型描述 获取变量的类型信息
DESCRIBE_BY_NAME( ) 根据类型全名获取类型描述 获取已知类型名的描述
DESCRIBE_BY_OBJECT_REF( ) 根据对象引用获取类描述 获取对象的类信息
DESCRIBE_BY_DATA_REF( ) 根据数据引用获取类型描述 获取引用指向的数据类型

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
DATA: lv_num    TYPE i VALUE 42,
lt_table TYPE STANDARD TABLE OF sflight.

" 根据数据对象获取描述
DATA(lo_type) = cl_abap_typedescr=>describe_by_data( lv_num ).
WRITE: / lo_type->type_kind, " 输出: 'I' (整数类型)
lo_type->absolute_name. " 输出: 'I'

" 根据类型名获取描述
DATA(lo_struct) = cl_abap_structdescr=>describe_by_name( 'SFLIGHT' ).

" 根据对象引用获取描述
DATA(lo_obj) = NEW zcl_my_class( ).
DATA(lo_class) = cl_abap_typedescr=>describe_by_object_ref( lo_obj ).
WRITE: / lo_class->absolute_name. " 输出: 'ZCL_MY_CLASS'

返回类型为 CL_ABAP_TYPEDESCR 的引用,可根据需要向下转型为具体子类。


题目3 - type_kind 属性值含义 [记忆]

CL_ABAP_TYPEDESCR 的 type_kind 属性返回单字符类型代码。请列出常见的 type_kind 值及其含义。

📌查看答案
type_kind 常见值
type_kind 含义 对应 ABAP 类型
I 整数 TYPE I
P 压缩小数 TYPE P
F 浮点数 TYPE F
C 字符 TYPE C / N
N 数字文本 TYPE N
D 日期 TYPE D
T 时间 TYPE T
X 十六进制 TYPE X
g 字符串 TYPE STRING
y 字节串 TYPE XSTRING
u 结构体(扁平) TYPES: BEGIN OF …
v 结构体(深层) 含字符串/内表组件
h 内表 TYPE TABLE / STANDARD TABLE
r 数据引用 TYPE REF TO DATA
l 对象引用 TYPE REF TO object
k 类引用 TYPE REF TO class
e 枚举类型 TYPE ENUM

题目4 - 结构体类型描述 [记忆]

如何使用 CL_ABAP_STRUCTDESCR 获取结构体的组件信息?请写出代码示例并说明返回的组件表结构。

📌查看答案
获取结构体组件信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
" 方式1:通过数据对象
DATA: ls_flight TYPE sflight.
DATA(lo_struct) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_data( ls_flight ) ).

" 方式2:通过类型名
DATA(lo_struct2) = CAST cl_abap_structdescr(
cl_abap_structdescr=>describe_by_name( 'SFLIGHT' ) ).

" 获取组件列表
DATA(lt_components) = lo_struct->get_components( ).

" 遍历组件
LOOP AT lt_components INTO DATA(ls_comp).
WRITE: / ls_comp-name, " 组件名(如 CARRID)
ls_comp-type->type_kind, " 类型代码(如 'C')
ls_comp-type->length. " 长度
ENDLOOP.

组件表(COMPONENT_TABLE)结构

字段 类型 说明
NAME STRING 组件名称
TYPE REF TO CL_ABAP_DATADESCR 组件的类型描述引用
AS_INCLUDE ABAP_BOOL 是否为 INCLUDE 结构
SUFFIX STRING INCLUDE 的后缀
DECIMALS I 小数位数(仅 P 类型)
TYPE_KIND TYPEKIND 内部类型代码

其他有用的方法

  • GET_COMPONENT( iv_name ) — 获取指定名称的组件
  • GET_SYMBOL_TABLE( ) — 获取符号表
  • STRUCT_KIND 属性 — 结构类型(FLAT/DEEP)

题目5 - 内表类型描述 [记忆]

如何使用 CL_ABAP_TABLEDESCR 获取内表的类型信息(如表类型、行类型、键定义)?

📌查看答案
内表类型描述
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
DATA: lt_flights TYPE SORTED TABLE OF sflight
WITH NON-UNIQUE KEY carrid connid.

" 获取内表类型描述
DATA(lo_table) = CAST cl_abap_tabledescr(
cl_abap_typedescr=>describe_by_data( lt_flights ) ).

" 获取行类型
DATA(lo_line_type) = lo_table->get_table_line_type( ).
WRITE: / '行类型:', lo_line_type->absolute_name. " SFLIGHT

" 获取表类别
CASE lo_table->table_kind.
WHEN cl_abap_tabledescr=>tablekind_std.
WRITE: / '标准表'.
WHEN cl_abap_tabledescr=>tablekind_sorted.
WRITE: / '排序表'.
WHEN cl_abap_tabledescr=>tablekind_hashed.
WRITE: / '哈希表'.
WHEN cl_abap_tabledescr=>tablekind_any.
WRITE: / '任意表类型'.
ENDCASE.

" 获取键信息
DATA(lt_keys) = lo_table->key.
LOOP AT lt_keys INTO DATA(ls_key).
WRITE: / '键组件:', ls_key-name.
ENDLOOP.

" 检查唯一性
IF lo_table->has_unique_key = abap_true.
WRITE: / '键是唯一的'.
ENDIF.

CL_ABAP_TABLEDESCR 关键属性

属性 说明
TABLE_KIND 表类别(STD/SORTED/HASHED)
KEY 键组件表
HAS_UNIQUE_KEY 键是否唯一
INITIAL_SIZE 初始大小

题目6 - describe_by_object_ref 使用 [记忆]

describe_by_object_refdescribe_by_class_name 有什么区别?在什么场景下使用 describe_by_object_ref

📌查看答案
两种方法对比
方法 输入 返回 用途
describe_by_object_ref 对象引用实例 对象实际运行时类的描述 获取对象运行时类型
describe_by_class_name 类名字符串 指定名称的类描述 按名称查询类信息

关键区别

  • describe_by_object_ref 返回的是对象的 实际运行时类型(考虑多态)
  • describe_by_class_name 返回的是 指定名称 的类描述

多态场景示例

1
2
3
4
5
6
7
8
9
10
11
12
DATA: lo_animal TYPE REF TO lcl_animal.

" 实际创建的是子类 lcl_dog 的实例
lo_animal = NEW lcl_dog( ).

" describe_by_object_ref 返回 lcl_dog 的描述
DATA(lo_type) = cl_abap_typedescr=>describe_by_object_ref( lo_animal ).
WRITE: / lo_type->absolute_name. " 输出: LCL_DOG(而非 LCL_ANIMAL)

" describe_by_class_name 返回指定类的描述
DATA(lo_type2) = cl_abap_classdescr=>describe_by_class_name( 'LCL_ANIMAL' ).
WRITE: / lo_type2->absolute_name. " 输出: LCL_ANIMAL

使用场景

  • 多态编程中确定对象的实际类型
  • 动态方法调用前检查对象是否实现了某个接口
  • 调试和日志记录中获取对象的类名

题目7 - RTTC 动态创建数据类型 [记忆]

什么是 RTTC(Run Time Type Creation)?如何使用 RTTC 动态创建结构体类型和内表类型?

📌查看答案
RTTC 概念
RTTC 允许在运行时动态创建新的数据类型,并使用这些类型创建数据引用。

动态创建结构体类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
" 定义组件
DATA(lt_components) = VALUE cl_abap_structdescr=>component_table(
( name = 'ID' type = cl_abap_elemdescr=>get_i( ) )
( name = 'NAME' type = cl_abap_elemdescr=>get_c( 20 ) )
( name = 'PRICE' type = cl_abap_elemdescr=>get_p(
p_length = 10 p_decimals = 2 ) )
).

" 创建结构体类型
DATA(lo_struct_type) = cl_abap_structdescr=>create( lt_components ).

" 创建数据引用并分配
DATA: lo_data TYPE REF TO data.
CREATE DATA lo_data TYPE HANDLE lo_struct_type.

" 使用字段符号访问
FIELD-SYMBOLS: <ls_data> TYPE any.
ASSIGN lo_data->* TO <ls_data>.
ASSIGN COMPONENT 'ID' OF STRUCTURE <ls_data> TO FIELD-SYMBOL(<lv_id>).
<lv_id> = 100.

动态创建内表类型

1
2
3
4
5
6
7
8
9
10
" 基于已创建的结构体类型创建内表类型
DATA(lo_table_type) = cl_abap_tabledescr=>create(
p_line_type = lo_struct_type
p_table_kind = cl_abap_tabledescr=>tablekind_std
p_unique = abap_false ).

" 创建内表数据引用
CREATE DATA lo_table TYPE HANDLE lo_table_type.
FIELD-SYMBOLS: <lt_table> TYPE ANY TABLE.
ASSIGN lo_table->* TO <lt_table>.

关键方法

  • CL_ABAP_ELEMDESCR=>GET_I( ) — 获取基本类型描述
  • CL_ABAP_ELEMDESCR=>GET_C( length ) — 获取字符类型描述
  • CL_ABAP_STRUCTDESCR=>CREATE( components ) — 创建结构体
  • CL_ABAP_TABLEDESCR=>CREATE( ... ) — 创建内表

题目8 - RTTI 与 RTTC 协作分析 [分析]

分析以下场景:一个程序需要从外部配置表读取字段定义(字段名、类型、长度),然后动态创建对应的内表并填充数据。请说明 RTTI 和 RTTC 在此场景中分别扮演什么角色,以及完整的处理流程。

📌查看答案
场景分析:RTTI + RTTC 协作

RTTI 的角色(读取已有类型信息)

  • 从配置表中读取的字段类型名(如 ‘CHAR’, ‘INT’)需要转换为对应的类型描述对象
  • 可使用 CL_ABAP_ELEMDESCR=>DESCRIBE_BY_NAME( ) 获取基本类型信息

RTTC 的角色(动态创建新类型)

  • 根据配置动态组合组件,创建新的结构体类型
  • 基于结构体类型创建内表类型
  • 使用 CREATE DATA ... TYPE HANDLE 创建实际的数据对象

完整流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
" 第1步:从配置表读取字段定义(数据层)
SELECT field_name, abap_type, length, decimals
FROM zfield_config
INTO TABLE @DATA(lt_config).

" 第2步:使用 RTTC 构建组件表
DATA: lt_components TYPE cl_abap_structdescr=>component_table.
LOOP AT lt_config INTO DATA(ls_conf).
DATA(lo_elem) = cl_abap_elemdescr=>get_c( ls_conf-length ).
" 根据类型分发
CASE ls_conf-abap_type.
WHEN 'CHAR'.
lo_elem = cl_abap_elemdescr=>get_c( ls_conf-length ).
WHEN 'INT'.
lo_elem = cl_abap_elemdescr=>get_i( ).
WHEN 'PACK'.
lo_elem = cl_abap_elemdescr=>get_p(
p_length = ls_conf-length p_decimals = ls_conf-decimals ).
ENDCASE.
APPEND VALUE #( name = ls_conf-field_name type = lo_elem )
TO lt_components.
ENDLOOP.

" 第3步:RTTC 创建动态结构体类型
DATA(lo_struct) = cl_abap_structdescr=>create( lt_components ).

" 第4步:RTTC 创建动态内表类型
DATA(lo_table) = cl_abap_tabledescr=>create( lo_struct ).

" 第5步:创建数据对象
DATA: lo_data_ref TYPE REF TO data.
CREATE DATA lo_data_ref TYPE HANDLE lo_table.
FIELD-SYMBOLS: <lt_dynamic> TYPE STANDARD TABLE.
ASSIGN lo_data_ref->* TO <lt_dynamic>.

" 第6步:填充数据(使用字段符号动态访问)
DATA(lo_row_ref) = cl_abap_structdescr=>create( lt_components ).
CREATE DATA lo_row TYPE HANDLE lo_row_ref.
ASSIGN lo_row->* TO FIELD-SYMBOL(<ls_row>).
ASSIGN COMPONENT 'FIELD1' OF STRUCTURE <ls_row>
TO FIELD-SYMBOL(<lv_val>).
<lv_val> = 'Hello'.
APPEND <ls_row> TO <lt_dynamic>.

总结:RTTI 识别现有类型 → RTTC 组合创建新类型 → CREATE DATA 实例化。


题目9 - 动态类型创建综合应用 [应用]

请编写完整的 ABAP 程序:根据输入参数动态创建一个包含任意数量字段的结构体,然后创建对应的内表,插入若干行数据,并使用 RTTI 输出内表的类型信息。

📌查看答案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
REPORT zrttc_dynamic_table.

PARAMETERS: p_fields TYPE i DEFAULT 3.

START-OF-SELECTION.
PERFORM create_dynamic_table.

FORM create_dynamic_table.
DATA: lt_components TYPE cl_abap_structdescr=>component_table.

" 动态构建组件
DO p_fields TIMES.
DATA(lv_name) = |FIELD{ sy-index }|.
DATA(lo_type) = COND #(
WHEN sy-index = 1 THEN cl_abap_elemdescr=>get_i( )
WHEN sy-index = 2 THEN cl_abap_elemdescr=>get_c( 30 )
WHEN sy-index = 3 THEN cl_abap_elemdescr=>get_p(
p_length = 10 p_decimals = 2 )
ELSE cl_abap_elemdescr=>get_c( 20 ) ).

APPEND VALUE #(
name = lv_name
type = lo_type
) TO lt_components.
ENDDO.

" 创建结构体和内表类型
DATA(lo_struct) = cl_abap_structdescr=>create( lt_components ).
DATA(lo_table_type) = cl_abap_tabledescr=>create(
p_line_type = lo_struct
p_table_kind = cl_abap_tabledescr=>tablekind_std ).

" 创建数据对象
DATA: lo_table_ref TYPE REF TO data,
lo_row_ref TYPE REF TO data.
CREATE DATA lo_table_ref TYPE HANDLE lo_table_type.
CREATE DATA lo_row_ref TYPE HANDLE lo_struct.

FIELD-SYMBOLS: <lt_table> TYPE STANDARD TABLE,
<ls_row> TYPE any.
ASSIGN lo_table_ref->* TO <lt_table>.
ASSIGN lo_row_ref->* TO <ls_row>.

" 插入数据
DO 5 TIMES.
ASSIGN COMPONENT 'FIELD1' OF STRUCTURE <ls_row>
TO FIELD-SYMBOL(<lv_f1>).
<lv_f1> = sy-index * 10.

ASSIGN COMPONENT 'FIELD2' OF STRUCTURE <ls_row>
TO FIELD-SYMBOL(<lv_f2>).
<lv_f2> = |记录 { sy-index }|.

APPEND <ls_row> TO <lt_table>.
ENDDO.

" 使用 RTTI 输出类型信息
DATA(lo_rtti) = cl_abap_tabledescr=>describe_by_data( <lt_table> ).
DATA(lo_tab_info) = CAST cl_abap_tabledescr( lo_rtti ).

WRITE: / '表类型:',
SWITCH string( lo_tab_info->table_kind
WHEN cl_abap_tabledescr=>tablekind_std THEN '标准表'
WHEN cl_abap_tabledescr=>tablekind_sorted THEN '排序表'
WHEN cl_abap_tabledescr=>tablekind_hashed THEN '哈希表' ).

WRITE: / '行数:', lines( <lt_table> ).
WRITE: / '--- 组件 ---'.
DATA(lo_line) = lo_tab_info->get_table_line_type( ).
LOOP AT CAST cl_abap_structdescr( lo_line )->get_components( )
INTO DATA(ls_comp).
WRITE: / ls_comp-name, ls_comp-type->type_kind.
ENDLOOP.
ENDFORM.

题目10 - RTTI 用于通用编程 [应用]

请编写一个通用工具方法,接收任意内表参数,自动输出内表的元信息(行数、列数、每列名称和类型)。这体现了 RTTI 在通用编程中的什么价值?

📌查看答案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
CLASS lcl_table_info DEFINITION.
PUBLIC SECTION.
CLASS-METHODS: display_info
IMPORTING it_data TYPE ANY TABLE.
ENDCLASS.

CLASS lcl_table_info IMPLEMENTATION.
METHOD display_info.
" 获取内表类型描述
DATA(lo_table) = CAST cl_abap_tabledescr(
cl_abap_typedescr=>describe_by_data( it_data ) ).

" 获取行类型
DATA(lo_line) = CAST cl_abap_structdescr(
lo_table->get_table_line_type( ) ).

" 获取组件
DATA(lt_comp) = lo_line->get_components( ).

" 输出元信息
WRITE: / '===== 内表元信息 ====='.

" 表类型
WRITE: / '表类型:',
SWITCH string( lo_table->table_kind
WHEN cl_abap_tabledescr=>tablekind_std THEN '标准表'
WHEN cl_abap_tabledescr=>tablekind_sorted THEN '排序表'
WHEN cl_abap_tabledescr=>tablekind_hashed THEN '哈希表' ).

" 行数和列数
WRITE: / '行数:', lines( it_data ).
WRITE: / '列数:', lines( lt_comp ).

" 列详情
WRITE: / '--- 列详情 ---'.
WRITE: / |{ '列名' WIDTH=20 } { '类型' WIDTH=6 } { '长度' WIDTH=6 }|.
WRITE: / '-------------------------------------------'.

LOOP AT lt_comp INTO DATA(ls_col).
DATA(lv_type) = SWITCH string( ls_col-type->type_kind
WHEN 'I' THEN 'INT'
WHEN 'C' THEN 'CHAR'
WHEN 'P' THEN 'PACK'
WHEN 'D' THEN 'DATE'
WHEN 'T' THEN 'TIME'
WHEN 'g' THEN 'STRG'
WHEN 'h' THEN 'TTAB'
ELSE ls_col-type->type_kind ).

WRITE: / |{ ls_col-name WIDTH=20 } { lv_type WIDTH=6 } |
&& |{ ls_col-type->length WIDTH=6 }|.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

" 使用示例
START-OF-SELECTION.
SELECT * FROM sflight INTO TABLE @DATA(lt_flights) UP TO 10 ROWS.
lcl_table_info=>display_info( lt_flights ).

RTTI 在通用编程中的价值

  1. 真正的多态TYPE ANY TABLE 使方法可接受任意内表,RTTI 在运行时确定具体类型
  2. 消除重复代码:一个方法处理所有内表,不需要为每种表结构写单独的版本
  3. 动态框架:这是 ALV、序列化框架等通用组件的基础技术
  4. 调试工具:快速检查任意数据对象的结构信息
  5. 元数据驱动:基于类型信息自动生成 UI、文档或映射逻辑

这种模式在企业级 ABAP 开发中非常常见,特别是在框架和工具类的开发中。


📌模式总结 (点击展开)
关键词 答案
RTTS 运行时类型服务总称(RTTI + RTTC)
RTTI 运行时类型识别(读取类型信息)
RTTC 运行时类型创建(动态构建类型)
CL_ABAP_TYPEDESCR 类型描述根类
CL_ABAP_ELEMDESCR 基本类型描述
CL_ABAP_STRUCTDESCR 结构体类型描述
CL_ABAP_TABLEDESCR 内表类型描述
describe_by_data 根据数据对象获取描述
describe_by_name 根据类型名获取描述
describe_by_object_ref 根据对象引用获取描述
type_kind 单字符类型代码(I/C/P/D/h 等)
get_components 获取结构体组件列表
get_table_line_type 获取内表行类型
CREATE(RTTC) 动态创建类型描述
CREATE DATA … TYPE HANDLE 根据类型描述创建数据
通用编程 TYPE ANY/TABLE + RTTI 实现多态工具