ABAP-OO:(8)泛型

 什么是泛型编程?       

        “泛型”机制在 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.

运行效果:

内容概要:本文介绍了一个针对电力系统连锁故障传播路径的N-k多阶段双层优化及故障场景筛选模,该模基于混合整数线性规划(MILP)方法构建,旨在全面评估电力系统在遭受多重故障时的脆弱性与恢复能力。通过引入故障传播路径的概念,模能够动态模拟故障在电网中的逐级扩散过程,并结合多阶段优化策略,实现对关键故障场景的有效识别与优先排序。整个框架不仅考虑了初始故障元件的选取,还涵盖了后续因潮流转移引发的级联跳闸行为,从而提升了风险评估的准确性与时效性。该研究已在Matlab平台上完成代码实现,具备良好的可复现性和工程应用价值,适用于提升现代电网的安全防御水平。; 适合人群:电力系统、能源安全及相关领域的科研人员、高校研究生以及从事电网规划与运行管理的工程技术人员。; 使用场景及目标:①用于电力系统安全评估中识别最危险的N-k故障组合;②支撑电网应急预案制定与薄弱环节改造;③作为学术研究中关于级联故障建模与优化求解的教学与验证工具;④服务于智能电网背景下抵御蓄意攻击或极端事件的风险防控决策。; 阅读建议:建议读者结合Matlab代码深入理解模的数学 formulation 与求解流程,重点关注目标函数设计、约束条件构建及双层优化结构的实现逻辑,同时可通过调整系统参数和故障设定进行仿真对比分析,以掌握不同因素对连锁故障演化的影响规律。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值