什么是泛型编程?
“泛型”机制在 ABAP 高级开发领域十分活跃,并且通过现代语法变得更加强大易用。它的未来在于更安全、更简洁的表达方式,深入理解和掌握新式的泛型和动态编程方法,必须装备的技能,在许多企业级 ABAP 项目的核心底层逻辑中,它们都是基石:
-
SAP 标准框架:例如,
SAP Gateway Foundation (SAP_GWFND)框架处理 OData 服务时,就需要处理结构不固定的数据。 -
通用服务开发:在开发通用日志、通用校验、通用映射等需要处理各种数据类型的服务时,泛型保证了代码的复用性。
-
现代开发模式:在 SAP BTP 的 ABAP 环境中,开发更灵活的 RAP(RESTful ABAP Programming Model)扩展时,也会频繁用到。
ABAP中的泛型工具
ABAP 中的泛型采用了预定义的通用类型 (Generic Type)与字段符号相配合来实现。
虽然泛型功能强大,但也潜藏着风险,SAP 官方指南ABAP_RULE 对此有明确建议:在参数类型定义时,要尽可能精确,只在有必要 时才使用泛型。附:ABAP类型和泛型对照表
| 泛型类型 | 含义及可接受类型 |
|---|---|
ANY/DATA | 任何数据类型 |
SIMPLE | 所有基本数据类型 |
NUMERIC | 数值类型 (I, F, P 等) |
CSEQUENCE | 字符型 (C, STRING) |
CLIKE | 类字符型 (C, N, D, T, STRING 等) |
STANDARD TABLE | 标准表 |
INDEX TABLE | 所有可按索引访问的表 (标准表和排序表) |
ANY TABLE | 所有类型的表 |
OBJECT | 任何对象引用 |
这个原则非常关键:
- 风险举例:一个最极端的例子是,如果给方法参数定义了泛型类型
ANY,那它几乎可以接收 ABAP 中的任意数据类型。然而,编译器无法检查这个“万能”参数是否真的有能被你的代码逻辑安全处理的属性或组件。这极易导致运行时错误,比如试图访问一个不存在的表字段。
ABAP泛型示例
泛型方法(使用泛型CLIKE为例):
CLASS zcl_generic_demo DEFINITION CREATE PUBLIC.
PUBLIC SECTION.
" 泛型方法:iv_data 可以传 字符/数字/字符串
METHODS print_data IMPORTING iv_data TYPE CLIKE.
ENDCLASS.
CLASS zcl_generic_demo IMPLEMENTATION.
METHOD print_data.
WRITE: / '泛型数据:', iv_data.
ENDMETHOD.
ENDCLASS.
" 主程序调用
START-OF-SELECTION.
DATA(lo_gen) = NEW zcl_generic_demo( ).
" 传字符
lo_gen->print_data( 'SAP ABAP 泛型' ).
" 传数字
lo_gen->print_data( '12345' ).
" 传字符串
lo_gen->print_data( |动态字符串{ sy-datum }| ).
运行结果:

内表泛型(ANY TABLE)代码示例:
CLASS zcl_generic_demo DEFINITION.
PUBLIC SECTION.
" 泛型方法:处理任意内表
METHODS loop_any_table IMPORTING it_table TYPE ANY TABLE.
ENDCLASS.
CLASS zcl_generic_demo IMPLEMENTATION.
METHOD loop_any_table.
FIELD-SYMBOLS <fs_row> TYPE any. " 指向表行
FIELD-SYMBOLS <fs_comp> TYPE any. " 指向行内组件
DATA lo_row_type TYPE REF TO cl_abap_structdescr.
" 获取行类型描述符
DATA(lo_tab_type) = cl_abap_typedescr=>describe_by_data( it_table ).
IF lo_tab_type->kind = cl_abap_typedescr=>kind_table.
DATA(lo_tab_descr) = CAST cl_abap_tabledescr( lo_tab_type ).
lo_row_type ?= lo_tab_descr->get_table_line_type( ).
ENDIF.
LOOP AT it_table ASSIGNING <fs_row>.
WRITE: / '=== 行数据 ==='.
IF lo_row_type IS BOUND AND lo_row_type->kind = cl_abap_typedescr=>kind_struct.
" 行是结构,遍历所有组件
DATA(components) = lo_row_type->get_components( ).
LOOP AT components INTO DATA(comp).
ASSIGN COMPONENT comp-name OF STRUCTURE <fs_row> TO <fs_comp>.
IF sy-subrc = 0.
WRITE: comp-name, ':', <fs_comp>.
ENDIF.
ENDLOOP.
ELSE.
" 行不是结构(例如单字段表),直接输出
WRITE: / <fs_row>.
ENDIF.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
" 主程序调用
START-OF-SELECTION.
DATA(lo_gen) = NEW zcl_generic_demo( ).
SELECT * INTO TABLE @DATA(lt_makt) FROM makt UP TO 5 ROWS.
IF lines( lt_makt ) NE 0.
lo_gen->loop_any_table( lt_makt )."可以是任意内表
ENDIF.
运行效果:可以尝试获取mara的数据

动态内表(泛型应用案例):
PARAMETERS: p_table TYPE tabname OBLIGATORY. " 运行时输入表名,如 SFLIGHT, SPFLI
" ----- 动态数据结构 -----
DATA: lo_struct_descr TYPE REF TO cl_abap_structdescr,
lo_tab_descr TYPE REF TO cl_abap_tabledescr,
lo_data_ref TYPE REF TO data.
FIELD-SYMBOLS: <ft_result> TYPE STANDARD TABLE.
" ----- 开始主流程 -----
START-OF-SELECTION.
" 1. 获取表的结构类型描述器(动态创建类对象)
lo_struct_descr ?= cl_abap_typedescr=>describe_by_name( p_table ).
" 2. 基于结构类型,创建对应的标准内表类型描述器
lo_tab_descr = cl_abap_tabledescr=>create( p_line_type = lo_struct_descr
p_table_kind = cl_abap_tabledescr=>tablekind_std ).
" 3. 根据上述类型描述器,动态创建内表并将引用存入指针
CREATE DATA lo_data_ref TYPE HANDLE lo_tab_descr.
" 4. 将动态内表绑定到字段符号,以便后续操作
ASSIGN lo_data_ref->* TO <ft_result>.
" 5. 执行动态查询并填充内表
IF <ft_result> IS ASSIGNED.
SELECT * FROM (p_table) INTO TABLE <ft_result> UP TO 50 ROWS.
IF sy-subrc = 0.
" 6. 使用 ALV 展示动态内表
TRY.
cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv)
CHANGING t_table = <ft_result> ).
lo_alv->display( ).
CATCH cx_salv_msg INTO DATA(lo_ex).
MESSAGE lo_ex->get_text( ) TYPE 'I'.
ENDTRY.
ENDIF.
ENDIF.
ABAP泛型高阶用法:动态编程
通过上面的案例可以看到,ABAP 泛型本质是通过预定义泛型类型 + 字段符号 + 数据引用,实现 “类型无关” 的代码复用。
使用 REF TO DATA + CREATE DATA 实现运行时创建任意类型数据,配合泛型方法传递,适合动态结构 / 内表场景。
简单示例代码:
CLASS zcl_dynamic_data DEFINITION CREATE PUBLIC.
PUBLIC SECTION.
METHODS process_dynamic IMPORTING io_data TYPE REF TO data.
ENDCLASS.
CLASS zcl_dynamic_data IMPLEMENTATION.
METHOD process_dynamic.
FIELD-SYMBOLS: <fs_data> TYPE any.
ASSIGN io_data->* TO <fs_data>.
WRITE: / '动态数据值:', <fs_data>.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA(lo_dyn) = NEW zcl_dynamic_data( ).
DATA: lo_data1 TYPE REF TO data,
lo_data2 TYPE REF TO data.
FIELD-SYMBOLS : <fs_any1> TYPE any.
FIELD-SYMBOLS : <fs_any2> TYPE any.
" 动态创建字符串
CREATE DATA lo_data1 TYPE string.
ASSIGN lo_data1->* to <fs_any1>.
<fs_any1> = '动态字符串'.
lo_dyn->process_dynamic( lo_data1 ).
" 动态创建数字
CREATE DATA lo_data2 TYPE i.
ASSIGN lo_data2->* to <fs_any2>.
<fs_any2> = 999.
lo_dyn->process_dynamic( lo_data2 ).
运行效果:

我们可以用SAP 提供的类的反射机制来演示泛型在类对象中的应用。 类对象反射机制可以让我们在程序的运行期(Runtime)获得一个类的具体细节,如类 的状态、变量名称和值、方法名称和参数等信息。还可以让我们在运行期实例化对象,通过调用 get/set 方法获取变量的值。
示例代码:
" 我们可以利用SAP提供的类的反射机制来演示泛型在类对象中的应用
" 类对象反射机制可以让我们在程序的运行期获得一个类的具体细节,如类的状态、变量名称和值、方法名称和参数等信息,
"还可以让我们在运行期实例化对象,通过调用get/set获取变量的值
" 可以调用SAP标准类CL_ABAP_CLASSDESCR方法获取类的属性和方法,并配合使用泛型来操作类对象反射
"定义局部类,包含两个方法和属性
CLASS lcl_c101 DEFINITION.
PUBLIC SECTION.
DATA:mv_string TYPE string VALUE 'String Of Class Attribute'.
DATA:mv_date TYPE string VALUE '2017.10.21'.
METHODS:print_string.
METHODS:print_date.
ENDCLASS.
"定义局部类的方法
CLASS lcl_c101 IMPLEMENTATION.
METHOD print_string.
WRITE:/ mv_string.
ENDMETHOD.
METHOD print_date.
WRITE:/ mv_date.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA go_ref TYPE REF TO object.
"根据参数类名动态创建实例对象
CREATE OBJECT go_ref TYPE ('LCL_C101').
"2.以下为类反射的实现
"调用SAP标准类CL_ABAP_CLASSDESCR方法获取类的属性和方法
DATA go_class_desc TYPE REF TO cl_abap_classdescr.
go_class_desc ?= cl_abap_classdescr=>describe_by_object_ref( go_ref ).
DATA gt_attr_desc TYPE abap_attrdescr_tab.
DATA gs_attr_desc TYPE abap_attrdescr.
DATA gt_meth_desc TYPE abap_methdescr_tab.
DATA gs_meth_desc TYPE abap_methdescr.
"定义字段符号,类型为任意类型,用于存储类的方法名称
"获取类的方法列表到内表中
gt_meth_desc[] = go_class_desc->methods.
WRITE:'===========动态访问类中的方法==============',/.
LOOP AT gt_meth_desc INTO gs_meth_desc.
"将类的方法名称赋予字段符号
ASSIGN gs_meth_desc-name TO FIELD-SYMBOL(<fs_meth>).
WRITE:'类的方法:', <fs_meth>,/.
ENDLOOP.
WRITE:'===========动态访问类中的属性==============',/.
FIELD-SYMBOLS <fs_attr> TYPE ANY.
"获取类的属性名称到内表中
gt_attr_desc[] = go_class_desc->attributes.
"动态访问类中的属性
LOOP AT gt_attr_desc INTO gs_attr_desc.
"将类的属性名称赋予字段符号
ASSIGN gs_attr_desc-name TO <fs_attr>.
WRITE:'类的属性:',<fs_attr>.
"将类的属性值赋予字段符号
ASSIGN go_ref->(gs_attr_desc-name) TO <fs_attr>.
WRITE:'属性值:', <fs_attr>,/.
ENDLOOP.
WRITE:'===========执行类的方法==============',/.
"动态访问类中的方法
LOOP AT gt_meth_desc INTO gs_meth_desc.
"动态调用类中的方法
CALL METHOD go_ref->(gs_meth_desc-name).
ENDLOOP.
运行效果:


1000

被折叠的 条评论
为什么被折叠?



