定义您自己的异常类

学习目标

  • 定义您自己的异常类。

自有异常类的定义

存在许多名称均以 cx_sy 开头的预定义系统异常类。但是,这些类描述了技术错误,并不反映应用程序的语义。

在航班应用程序中,类可能需要描述运输公司与转机编号特定组合不存在的异常。

要定义自己的异常类,请创建继承自类 cx_static_check 的新类。

在本视频中,您将了解如何创建、准备、附加和引发异常。

视频:本节含 SAP 官方视频(视频 ID 1_4dhp1597),需在 learning.sap.com 在线观看。

试用:定义您自己的异常类

创建异常类 ZSD4D01_EXCEPTIONS,如下所示:

  1. 按 Ctrl + Shift + N 创建新的 ABAP 资源库对象。

  2. 输入过滤器文本 消息 ,然后双击搜索结果清单中的条目 消息类 。

  3. 将消息类分配到有效包。输入名称 ZS4D401_EXCEPTIONS 和描述,然后按 下一步 。

  4. 选择相应的传输请求,然后选择 完成 。

  5. 输入消息编号 001 和文本 连接 &1 &2 不存在 。

  6. 保存消息类。

  7. 创建实施接口 IF_OO_ADT_CLASSRUN 的新类。

  8. 切换到 本地类型 标签并将以下代码片段粘贴到编辑器中:

CLASS lcx_no_connection DEFINITION INHERITING FROM cx_static_Check.
PUBLIC SECTION.
interfaces if_t100_message.
METHODS constructor
IMPORTING
textid LIKE if_t100_message=>t100key OPTIONAL
previous LIKE previous OPTIONAL
airlineid TYPE /DMO/CARRIER_ID OPTIONAL
connectionnumber TYPE /DMO/CONNECTION_ID OPTIONAL.
CONSTANTS:
BEGIN OF lcx_no_connection,
msgid TYPE symsgid VALUE 'ZS4D401_EXCEPTIONS',
msgno TYPE symsgno VALUE '001',
attr1 TYPE scx_attrname VALUE 'AIRLINEID',
attr2 TYPE scx_attrname VALUE 'CONNECTIONNUMBER',
attr3 TYPE scx_attrname VALUE 'attr3',
attr4 TYPE scx_attrname VALUE 'attr4',
END OF lcx_no_connection.
DATA airlineid TYPE /dmo/carrier_id READ-ONLY.
DATA connectionnumber TYPE /dmo/connection_id READ-ONLY.
ENDCLASS.
CLASS lcx_no_Connection IMPLEMENTATION.
METHOD constructor.
super->constructor( previous = previous ).
me->airlineid = airlineid.
me->connectionnumber = connectionnumber.
CLEAR me->textid.
IF textid IS INITIAL.
if_t100_message~t100key = lcx_no_connection.
ELSE.
if_t100_message~t100key = textid.
ENDIF.
ENDMETHOD.
ENDCLASS.
CLASS lcl_connection DEFINITION.
PUBLIC SECTION.
METHODS constructor
IMPORTING
i_airlineid TYPE /dmo/carrier_id
i_connectionnumber TYPE /dmo/connection_id
RAISING lcx_no_connection.
PRIVATE SECTION.
DATA AirlineId TYPE /dmo/carrier_id.
DATA ConnectionNumber TYPE /dmo/connection_id.
DATA fromAirport TYPE /dmo/airport_from_id.
DATA toAirport TYPE /dmo/airport_to_id.
ENDCLASS.
CLASS lcl_Connection IMPLEMENTATION.
METHOD constructor.
DATA fromairport TYPE /dmo/airport_from_Id.
DATA toairport TYPE /dmo/airport_to_id.
SELECT SINGLE FROM /dmo/connection
FIELDS airport_from_id, airport_to_id
WHERE carrier_id = @i_airlineid
AND connection_id = @i_connectionnumber
INTO ( @fromairport, @toairport ).
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE lcx_no_connection
EXPORTING
airlineid = i_airlineid
connectionnumber = i_connectionnumber.
ELSE.
me->connectionnumber = i_connectionnumber.
me->fromairport = fromairport.
me->toairport = toairport.
ENDIF.
ENDMETHOD.
ENDCLASS.
  1. 切换到 全局类 标签并将以下代码片段粘贴到 main( ) 方法的实施中:

DATA connection TYPE REF TO lcl_connection.
DATA exception TYPE REF TO lcx_no_connection.
TRY.
connection = NEW #( i_airlineid = 'XX' i_connectionnumber = '0000' ).
CATCH lcx_no_connection into exception.
out->write( exception->get_text( ) ).
ENDTRY.
  1. 按 Ctrl + F3 激活类。

  2. 按 F9 执行类。浏览源代码以熟悉它。

定义和使用异常类

您发现异常文本对于应用程序不够具体。因此,您可以定义自己的异常类并将其用于代码中。

模板:

  • /LRN/CL_S4D401_EXS_HANDLING(全局类)

解决方案:

  • /LRN/CL_S4D401_EXS_CLASS(全局类)

  • /LRN/S4D401_EXS_MESS(消息类)

  • /LRN/CX_S4D401_EXS_FAILED(全局异常类)

任务 1: 复制模板(可选)

复制模板类 /LRN/CL_S4D401_EXS_HANDLING。如果已完成上一练习,则可以跳过此任务并继续编辑类 ZCL_##_SOLUTION

步骤

  1. 将类 /LRN/CL_S4D401_EXS_HANDLING 复制到您自己的包中的类(建议名称:ZCL_##_SOLUTION,其中 ## 代表组编号)。

  2. 在项目浏览器中,右键单击类 /LRN/CL_S4D401_EXS_HANDLING 以打开上下文菜单。

  3. 从上下文菜单中,选择复制....

  4. 在包字段中输入的名称。在名称字段中,输入 ZCL_##_SOLUTION,其中 ## 代表组编号。

  5. 选择下一步

  6. 确认传输请求并选择完成

  7. 激活复制。

  8. Ctrl + F3 激活类。

任务 2: 创建消息类

创建新的消息类(建议名称:Z##_MESSAGES,其中 ## 是您的组编号)。定义两个消息文本,一个用于报告不存在的载体,另一个用于报告缺少的读取权限。在这两条消息中,使用占位符作为承运方标识。

步骤

  1. 创建新的消息类(建议名称:Z##_MESSAGES,其中 ## 是您的组编号)。

  2. 在项目浏览器中,右键单击您的包并选择 新建其他 ABAP 资源库对象

  3. 在搜索字段中,输入**消息**。

  4. 从搜索结果中选择文本消息类,然后选择下一步

  5. 确保字段包含包的名称。

  6. 名称字段中,输入 Z##_MESSAGES,其中 ## 代表组编号。

  7. 描述字段中,输入**异常类的消息**。

  8. 选择下一步

  9. 确认传输请求并选择完成

  10. 定义消息以报告运输公司不存在(建议编号:010)。使用占位符作为承运方标识。

  11. 在第一行的编号列中,选择 <输入新值> 并输入 010

  12. 在同一行的*"短文本*"列中,输入**运输公司 &1 不存在**。

  13. 定义消息以报告用户无权显示特定运输公司(建议编号:020)。使用占位符作为承运方标识。

  14. 在第二行的编号列中,选择 <输入新值> 并输入 020

  15. 在同一行的短文本列中,输入**您无权显示运输公司 &1**。

  16. 保存消息类。

注意

既不需要也不可能激活消息类。对于消息类,我们不区分活动版本和非活动版本。

  1. Ctrl + S 保存消息类。

任务 3: 创建异常类

创建继承自超类 CX_STATIC_CHECK 的新全局异常类(建议名称:ZCX_##_FAILED,其中 ## 是您的组编号)。定义参考刚创建的消息文本的两个文本标识常量。声明公共只读实例属性(建议名称:CARRIER_ID),并确保在运行时将消息中的占位符替换为此属性的值。最后,使用可选导入参数扩展 CONSTRUCTOR 方法以设置新属性。

步骤

  1. 创建继承自超类 CX_STATIC_CHECK 的新全局类(建议名称:ZCX_##_FAILED,其中 ## 是您的组编号)。

  2. 在项目浏览器中,右键单击包并选择 新建ABAP 类

  3. 确保字段包含包的名称。

  4. 名称字段中,输入 ZCX_##_FAILED,其中 ## 代表组编号。

  5. 描述字段中,输入**实例化失败**。

  6. 超类字段中,输入 CX_STATIC_CHECK

  7. 选择下一步

  8. 确认传输请求并选择完成

  9. 使用代码完整化定义新的文本标识常量,即公共结构化常量。

  10. 在异常类的公共部分,在 INTERFACES 语句后输入**文本**,然后按 **Ctrl + 空格**键调用代码完整化。

  11. 从建议列表中选择 textIDExceptionClass - Default text id for exception class

  12. 代码完整化应按如下所示调整代码:

PUBLIC SECTION.
INTERFACES if_t100_message.
INTERFACES if_t100_dyn_msg.
CONSTANTS:
BEGIN OF zcx_##_failed,
msgid TYPE symsgid VALUE 'msgid',
msgno TYPE symsgno VALUE 'msgno',
attr1 TYPE scx_attrname VALUE 'attr1',
attr2 TYPE scx_attrname VALUE 'attr2',
attr3 TYPE scx_attrname VALUE 'attr3',
attr4 TYPE scx_attrname VALUE 'attr4',
END OF zcx_##_failed.
  1. 使用快速修复重命名常量(建议的新名称:carrier_not_exists)。

  2. 在 BEGIN OF 之后,将光标放在 zcx_##_failed 上(其中 ## 是您的组编号),然后按 Ctrl + 1 调用可用的快速修复。

  3. 从可用快速修复列表中,选择重命名 zcx_##_failed

  4. 当常量名称仍突出显示时,输入新名称。

  5. 调整常量的组件值。使用消息类的名称和第一条消息的编号作为值。

  6. 如下调整代码:

CONSTANTS:
BEGIN OF carrier_not_exist,
msgid TYPE symsgid      VALUE 'Z##_MESSAGES',
msgno TYPE symsgno      VALUE '010',
attr1 TYPE scx_attrname VALUE 'attr1',
attr2 TYPE scx_attrname VALUE 'attr2',
attr3 TYPE scx_attrname VALUE 'attr3',
attr4 TYPE scx_attrname VALUE 'attr4',
END OF carrier_not_exist.
  1. 使用相同的代码模板和快速修复为第二个消息文本定义另一个文本标识常量(建议名称:carrier_no_read_auth)。

  2. 添加以下代码:

CONSTANTS:
BEGIN OF carrier_no_read_auth,
msgid TYPE symsgid      VALUE 'Z##_MESSAGES',
msgno TYPE symsgno      VALUE '020',
attr1 TYPE scx_attrname VALUE 'attr1',
attr2 TYPE scx_attrname VALUE 'attr2',
attr3 TYPE scx_attrname VALUE 'attr3',
attr4 TYPE scx_attrname VALUE 'attr4',
END OF carrier_no_read_auth.
  1. 定义类型为 /DMO/CARRIER_ID 的公共只读实例属性(建议名称:CARRIER_ID)。

  2. 在异常类的公共部分,添加以下代码:

DATA carrier_id TYPE /dmo/carrier_id READ-ONLY.
  1. 调整文本标识常量。使用新属性的名称作为组件**属性 1** 的值。

  2. 如下调整代码:

CONSTANTS:
BEGIN OF carrier_not_exist,
msgid TYPE symsgid VALUE 'Z##_MESSAGES',
msgno TYPE symsgno VALUE '010',
attr1 TYPE scx_attrname VALUE 'carrier_id',
attr2 TYPE scx_attrname VALUE 'attr2',
attr3 TYPE scx_attrname VALUE 'attr3',
attr4 TYPE scx_attrname VALUE 'attr4',
END OF carrier_not_exist.
CONSTANTS:
BEGIN OF carrier_no_read_auth,
msgid TYPE symsgid VALUE 'Z##_MESSAGES',
msgno TYPE symsgno VALUE '020',
attr1 TYPE scx_attrname VALUE 'carrier_id',
attr2 TYPE scx_attrname VALUE 'attr2',
attr3 TYPE scx_attrname VALUE 'attr3',
attr4 TYPE scx_attrname VALUE 'attr4',
END OF carrier_no_read_auth.
  1. 扩展 CONSTRUCTOR 方法的签名。添加可用于设置属性 carrier_id 的值的可选导入参数(建议名称:carrier_id)。

  2. 如下调整代码:

METHODS constructor
IMPORTING
!textid    LIKE if_t100_message=>t100key OPTIONAL
!previous  LIKE previous OPTIONAL
carrier_id LIKE carrier_id OPTIONAL.

注意

您可以忽略或删除感叹号 (!)在现有参数名称之前。只要参数名称可与 ABAP 关键字区分开来,它们就是可选的。在全局类中,缺省情况下,该工具会插入它们。

  1. 调整 CONSTRUCTOR 方法的实施。在方法结束时,检查可选参数 carrier_id 是否为初始值。如果不是初始值,则将其值存储在实例属性 carrier_id 中。

注意

如果为导入参数和 属性选择相同的名称,则必须使用自参考我寻址属性。

  1. 如下调整代码:

IF textid IS INITIAL.
if_t100_message~t100key = if_t100_message=>default_textid.
ELSE.
if_t100_message~t100key = textid.
ENDIF.
IF carrier_id IS NOT INITIAL.
me->carrier_id = carrier_id.
ENDIF.
ENDMETHOD.
  1. 激活异常类。

  2. Ctrl + F3 激活代码。

任务 4: 使用异常类

在本地类 LCL_CARRIER 的方法 GET_INSTANCE 中,将异常类 CX_ABAP_INVALID_VALUECX_ABAP_AUTH_CHECK_EXCEPTION 替换为您自己的异常类。按照警告调整方法定义的 RAISING 子句和方法 IF_OO_ADT_CLASSRUN~MAIN 中的异常处理。

步骤

  1. 导航到本地类 LCL_CARRIER 的方法 GET_INSTANCE 的实施。

  2. 例如,在 大纲 视图中展开 ZCL_##_SOLUTIONLCL_CARRIER ,然后选择 GET_INSTANCE

  3. 移除或注释使用异常类 CX_ABAP_INVALID_VALUE 的 RAISE EXCEPTION 语句。

  4. 选择 RAISE EXCPETION 语句(包括参数传递),然后按 Ctrl + < 在所选行前添加注释符号。

  5. 使用您自己的异常类实施 RAISE EXCEPTION 语句。使用代码完整化插入 CONSTRUCTOR 的完整签名。

  6. 将光标置于带注释的代码之后,然后输入**引发异常类型 ZCX_##** ,其中 ## 是您的组编号。

  7. 按 **Ctrl + 空格**键调用代码完整化。

  8. Shift + Enter 插入完整签名。

  9. 确保代码完整化添加了以下代码:

RAISE EXCEPTION TYPE zcx_##_failed
*    EXPORTING
*      textid     =
*      previous   =
*      carrier_id =
  1. 为可选 textid 参数提供第一个文本标识常量,为可选 carrier_id 参数提供值 i_carrier_id

  2. 如下调整代码:

RAISE EXCEPTION TYPE zcx_##_failed
EXPORTING
textid     = zcx_##_failed=>carrier_not_exist
*     previous   =
carrier_id = i_carrier_id.
  1. 同样,在第二个 RAISE EXCEPTION 语句中使用您自己的异常类。这次使用其他文本标识常量作为参数 textid 的输入。

  2. 如下调整代码:

*  RAISE EXCEPTION TYPE cx_abap_auth_check_exception
*    EXPORTING
*      textid = cx_abap_auth_check_exception=>missing_authorization.
RAISE EXCEPTION TYPE zcx_##_failed
EXPORTING
textid     = zcx_##_failed=>carrier_no_read_auth
*     previous   =
carrier_id = i_carrier_id.
  1. 使用快速修复将您自己的异常类添加到方法 GET_INSTANCE 的 RAISING 子句中。

  2. 转到包含两个 RAISE EXCEPTION 语句的代码行,然后选择左侧带有灯泡的警告图标以调用可用的快速修复。

  3. 从可用快速修复列表中,选择添加引发声明

  4. 从方法 GET_INSTANCE 的 RAISING 子句中移除或注释冗余异常。

  5. 如下调整代码:

CLASS-METHODS get_instance
IMPORTING
i_carrier_id    TYPE /dmo/carrier_id
RETURNING
VALUE(r_result) TYPE REF TO lcl_carrier
RAISING
*      cx_abap_invalid_value
*      cx_abap_auth_check_exception
zcx_##_failed.
  1. 调整方法 IF_OO_ADT_CLASSRUN~MAIN 中的异常处理。将两个 CATCH 块替换为您自己的异常类的一个 CATCH 块。

  2. 如下调整代码:

TRY.
DATA(carrier)  = lcl_carrier=>get_instance( i_carrier_id = c_carrier_id ).
out->write( name = `Carrier Overview`
data = carrier->get_output(  ) ).
*    CATCH cx_abap_invalid_value INTO DATA(exc_inv).
*      out->write( exc_inv->get_text( ) ).
*    CATCH cx_abap_auth_check_exception INTO DATA(exc_auth).
*      out->write( exc_auth->get_text( ) ).
CATCH zcx_##_failed INTO DATA(exc_fail).
out->write( exc_fail->get_text( ) ).
ENDTRY.
  1. 激活并测试您的代码。调整常量 c_carrier_id 的值以引发不同的异常。

  2. Ctrl + F3 激活代码。

  3. F9 以控制台应用形式执行代码。

  4. 将常量 c_carrier_id 的值设置为 XX 可引发无效值的异常。

  5. 将常量 c_carrier_id 的值设置为 UA,以引发缺少权限的异常。