泛型类型与动态访问

泛型与动态访问 (★★)

#dynamic-prog #data-types #data-reference

概述

泛型数据类型不能直接用于声明数据对象。它们用作形式参数类型和字段符号类型,实际类型在运行时从赋值的数据对象中解析。这是编写灵活、可复用 ABAP 代码的基础。

ABAP 泛型数据类型

泛型 覆盖的完整类型 说明
any 所有数据类型 最泛型;不保证任何操作
any table 所有表类型 泛型内表
c c, d, n, t, string 类字符型
clike c, d, n, t, string 类字符型,可用于字符串函数
csequence c, string 字符序列(用于字符串函数)
data 所有数据类型 any 相同
decfloat decfloat16, decfloat34 十进制浮点
index table 标准、排序表 支持索引访问的表
n n 数字文本
numeric i, f, p, decfloat16, decfloat34 可进行数值计算
object 所有对象引用 泛型对象引用
simple 基本类型 + 结构(平坦) 可比较和排序
sorted table 仅排序表 特定表类别
standard table 仅标准表 特定表类别
table 所有表类型 any table 相同
x x, xstring 类字节型
xsequence x, xstring 字节序列
📌不能用泛型声明数据对象
DATA: lv_var TYPE any.不允许。泛型仅对以下有效:
  • 过程的形式参数(子程序、FM、方法)
  • 字段符号
  • 类型引用(TYPE REF TO data

泛型类型参数

当形式参数具有泛型类型时,具体类型在运行时从传入的实际参数确定:

1
2
3
4
5
6
FORM process USING p_data TYPE any.
" p_data 继承实际参数的类型
ENDFORM.

PERFORM process USING lv_string. " p_data 表现为 STRING
PERFORM process USING lv_int. " p_data 表现为 I
上下文 类型解析
FORM ... USING p TYPE any 调用时从实际参数
方法参数 TYPE any 调用时从实际参数
FM 参数 TYPE any 调用时从实际参数

泛型类型字段符号

字段符号从赋值的数据对象继承其类型:

1
2
3
4
FIELD-SYMBOLS: <fs> TYPE any.

ASSIGN lv_string TO <fs>. " <fs> 现在表现为 STRING
ASSIGN lv_int TO <fs>. " <fs> 现在表现为 I
💡字段符号与泛型
泛型类型的字段符号是一个”占位符”,采用所赋值对象的类型和值。这是泛型数据访问的主要机制。

泛型数据引用

1
DATA: dref TYPE REF TO data.
属性 行为
声明 TYPE REF TO data 创建泛型数据引用
赋值 可通过 GET REFERENCE OF ... INTO dref 指向任何数据对象
解引用 不能直接使用 dref->*;必须使用 ASSIGN
⚠️泛型数据引用解引用
TYPE REF TO data 变量不能直接用 ->* 解引用。必须使用 ASSIGN dref->* TO <fs> 来访问被引用的数据。
1
2
3
4
5
6
DATA: dref TYPE REF TO data.
FIELD-SYMBOLS: <fs> TYPE any.

CREATE DATA dref TYPE string.
ASSIGN dref->* TO <fs>. " 正确:使用 ASSIGN
<fs> = 'Hello'. " 通过字段符号访问

使用字段符号的类型转换

ASSIGN … CASTING

将赋值数据对象的内存区域视为具有不同类型:

1
2
3
FIELD-SYMBOLS: <hex> TYPE x.

ASSIGN lv_string TO <hex> CASTING. " 将字符串内存解释为原始字节
CASTING 变体 语法 效果
隐式类型 ASSIGN dobj TO <fs> CASTING 使用字段符号的声明类型
显式类型 ASSIGN dobj TO <fs> CASTING TYPE typ 使用指定类型
显式小数 ASSIGN dobj TO <fs> CASTING TYPE p DECIMALS 2 带小数的压缩数
💡CASTING 不转换数据
CASTING 以不同类型重新解释相同的内存内容。它转换数据。实际类型转换请使用转换函数。

ASSIGN … CASTING TYPE(动态)

1
ASSIGN lv_data TO <fs> CASTING TYPE (lv_type_name).

类型名称在运行时动态解析。

动态 ASSIGN

结构中的动态组件访问

1
2
3
4
5
6
7
8
9
10
FIELD-SYMBOLS: <comp> TYPE any.

" 按组件索引
ASSIGN COMPONENT 3 OF STRUCTURE ls_struct TO <comp>.

" 按组件名(动态)
ASSIGN COMPONENT 'FIELD_NAME' OF STRUCTURE ls_struct TO <comp>.

" 按变量名
ASSIGN COMPONENT lv_comp_name OF STRUCTURE ls_struct TO <comp>.
变体 语法 使用场景
按索引 ASSIGN COMPONENT n OF STRUCTURE 遍历所有组件
按名称(字面量) ASSIGN COMPONENT 'NAME' OF STRUCTURE 已知组件名,静态
按名称(变量) ASSIGN COMPONENT lv_name OF STRUCTURE 组件名在运行时确定

动态属性访问

1
2
3
4
5
6
7
FIELD-SYMBOLS: <attr> TYPE any.

" 动态访问类属性
ASSIGN (lcl_my_class=>lv_attr_name) TO <attr>.

" 通过对象引用访问实例属性
ASSIGN oref->('ATTRIBUTE_NAME') TO <attr>.
📌动态名称的括号语法
括号中的类/属性名遵循与其他动态令牌相同的规则:(name),内部不能有空格

动态结构组件访问模式

1
2
3
4
5
6
7
8
9
10
11
12
" 泛型:处理任意结构的所有组件
FIELD-SYMBOLS: <comp> TYPE any.
DATA: lo_struct TYPE REF TO cl_abap_structdescr.
DATA: lt_comp TYPE cl_abap_structdescr=>component_table.

lo_struct ?= cl_abap_typedescr=>describe_by_data( ls_any_struct ).
lt_comp = lo_struct->get_components( ).

LOOP AT lt_comp INTO DATA(ls_comp).
ASSIGN COMPONENT ls_comp-name OF STRUCTURE ls_any_struct TO <comp>.
" 泛型处理 <comp> ...
ENDLOOP.

使用数据引用的类型转换

操作 语法 说明
向上转型 oref_super = oref_sub 宽化;始终安全
向下转型 oref_sub ?= oref_super 窄化;可能抛出 CX_SY_MOVE_CAST_ERROR
数据引用向上转型 dref_any = dref_typed 安全:从特定到泛型
数据引用向下转型 dref_typed ?= dref_any 有风险:必须检查兼容性
1
2
3
4
5
6
7
8
9
" 向上转型(安全)
DATA: dref_any TYPE REF TO data.
DATA: dref_str TYPE REF TO string.

CREATE DATA dref_str TYPE string.
dref_any = dref_str. " 向上转型:始终成功

" 向下转型(有风险)
dref_str ?= dref_any. " 向下转型:可能抛出异常
⚠️向下转型需要 ?= 运算符
引用的窄化赋值需要 ?=(或内联的 CAST #(...))。标准 = 仅适用于向上转型。使用 TRY ... CATCH cx_sy_move_cast_error 确保安全。

比较:字段符号 vs 数据引用

方面 字段符号 数据引用
声明 FIELD-SYMBOLS <fs> TYPE any DATA dref TYPE REF TO data
赋值 ASSIGN dobj TO <fs> GET REFERENCE OF dobj INTO dref
访问 直接:<fs> 通过解引用:ASSIGN dref->* TO <fs>
可传递给 FM 否(不是数据对象) 是(是数据对象)
可在内表中
生命周期 直到取消赋值或超出作用域 直到被垃圾回收
类型转换 ASSIGN ... CASTING 使用 ?= 向上/向下转型

考试/测试模式

关键词 / 短语 答案
“泛型不能声明数据对象” anydataany tableclikesimple
“字段符号类型在运行时确定” 从赋值的数据对象继承类型
“TYPE REF TO DATA 解引用” 必须使用 ASSIGN dref->* TO <fs>;不能直接用 ->*
“ASSIGN COMPONENT 语法” ASSIGN COMPONENT (n|name) OF STRUCTURE struct TO <fs>
“动态属性访问” ASSIGN (class=>attr) TO <fs>ASSIGN oref->('ATTR') TO <fs>
“CASTING 不转换” 以不同类型重新解释相同内存
“引用向下转型运算符” ?=(窄化);可能抛出 CX_SY_MOVE_CAST_ERROR
“泛型引用 vs 类型化引用” TYPE REF TO data 是泛型;TYPE REF TO string 是完整的

相关笔记