定义和调用方法

学习目标

  • 定义和调用方法。

方法定义

在类的定义部分,使用 METHODS 定义实例方法,并使用 CLASS-METHODS 定义静态方法。方法的名称后跟方法的签名;即方法与其调用者交换的值集以及方法期间可能出现的异常。

方法的签名由参数和异常组成。每个参数都有一个名称和类型。

ABAP 知道以下类型的参数:

导入参数

导入参数是方法从调用者处接收的值。方法可以具有任意数量的导入参数。

默认情况下,导入参数是必需的,但有两种方法可以将其设置为可选:

  • 使用 OPTIONAL 附加。参数是可选的,其缺省值是适用于参数类型的初始值

  • 使用 DEFAULT 附加项。参数是可选的,其缺省值是您指定为 的值。

在方法内部,无法更改导入参数。如果尝试执行此操作,将导致语法错误。

导出参数

导出参数是由 方法返回的结果。方法可以具有任意数量的导出参数。所有导出参数都是可选的 - 调用程序仅使用其实际需要的值。

更改参数

更改参数是方法从调用者处接收的值。与导入参数不同, 方法可以更改这些参数的值。然后将它们返回给同名的调用者。方法可以具有任意数量的更改参数。缺省情况下,必须更改参数;您可以按照与导入参数相同的方式将其设置为可选。

返回参数

返回参数是可以直接在表达式中使用的方法结果。方法只能有一个返回参数。返回参数必须使用一种特殊形式的参数传递,称为逐值传递。这种形式的参数传递是通过用括号括住参数名定义的(无空格!)并在其前面使用关键字 VALUE。

关键字 RAISING 用于列出方法可能引发的异常以指示错误情况。然后,调用程序可以对错误做出反应。

例如,将两种方法添加到类 lcl_connection。

设置属性 carrier_idconnection_id 值的方法 set_attributes( ) 和返回这些属性值的 get_attributes( ) 方法。set_attributes( ) 需要两个导入参数,每个属性一个,get_attributes( ) 方法需要两个导出参数。

对于定义的每个方法,还必须在类的实施部分中创建实施。在此之前,您会在类中看到语法错误;语法检查会告诉您缺少方法的实施。

提示

ADT 提供快速修复以添加缺少的实施。要使用此快速修复,请执行以下操作:

  1. 将光标定位在含错误的 METHODS 语句中,然后按 Ctrl + 1。2. 从可能的快速修复清单中,选择添加实施... 并按回车。如果多个方法缺少实施,则快速修复标题为添加... 未实施方法。

如何定义方法

演示观看演示(SAP 官方交互式练习)

方法实施

您必须实施定义的每种方法。您可以通过添加一对语句 METHOD <method_name> 和 ENDMETHOD 在类的实施部分执行此操作。

方法实施包含可访问方法参数的 ABAP 语句(不允许更改导入参数)以及在类中声明的所有类型、属性和常量,与其可见性无关。实例方法可以访问实例属性和静态属性。静态方法只能访问静态组件。

在类的实施部分内,可以访问不含参考变量的类的属性和 '->'(对于静态属性,可访问类名称和 '=>')。

只有在实例方法的实施中,ABAP 才提供内置变量 ME。ME 是参考变量,使用当前类键入,在运行时使用当前实例的地址填充。ME 的使用是可选的,应避免使用,除非属性的名称与另一个数据对象的名称冲突,例如参数名称。

在此示例中,方法 set_attributes() 的导入参数与相应属性具有相同的名称。在这种情况下,名称本身表示参数。只有将 me-> 放在前面,才能清楚地看到将访问当前实例的属性。

如果在执行方法期间发生错误,该方法可以使用语句 RAISE EXCEPTION TYPE 后跟异常名称来触发异常。

注意

从技术上讲,异常名称是特殊 ABAP 类的名称,语句 RAISE EXCEPTION TYPE 创建参考类的实例。实例称为异常对象,特殊类型的 ABAP 类称为异常类。

引发异常时,方法执行终止。

如果满足所有前提条件,则将控制返回到调用程序。否则,整个程序会因运行时错误而终止。

继续程序的前提条件如下:

  • 引发的异常在方法签名的 RAISING 子句中声明

  • 方法调用由 TRY…ENDTRY 控制结构包围

  • TRY…ENDTRY 控制结构包含异常的 CATCH 块

注意

对于某些异常,当引发尚未在方法的 RAISING 子句中声明的异常时,ABAP 编辑器会显示语法警告以及快速修复。

如何实施方法

演示观看演示(SAP 官方交互式练习)

方法调用

使用参考变量和实例组件选择器 (->) 调用实例方法。组件选择器后跟要调用的方法的名称。对于静态方法,可使用类名称和静态组件选择器 (=>)。在这两种情况下,参数传递都在一对括号中进行。在任何情况下都需要括号。如果不传递任何参数,它们将留空。

注意

方法名称和左括号之间不允许有空格。另一方面,在左括号之后和右括号之前至少需要一个空格。

由于被调用方法导入的值由调用程序导出,因此会在关键字 EXPORTING 之后列出导入参数。参数名称在等号 (=) 的左侧列出,表达式在右侧列出。此表达式可以像文字、常量或变量一样简单,但只要表达式的类型与参数的类型匹配,也允许使用任何其他表达式。

导出参数列在关键字 IMPORTING 之后。请注意,对于导出参数,参数名称也列在等号的左侧。右侧的变量必须具有与参数相同的类型。

更改参数列在关键字 CHANGING 之后。右侧的变量类型必须与左侧的参数类型匹配。

此示例说明了实例方法 set_attributes( )get_attributes( ) 的调用。参数名称始终位于等号 (=) 的左侧。

提示

您可以使用 ADT 中的代码完整化生成包括参数传递在内的方法调用。为此,请执行以下操作:

  1. 键入参考变量(或类名称)和组件选择器。2. 键入部件选择器后,按 Ctrl + Space 显示可以寻址的部件列表。3. 选择方法,然后按 Shift + Enter 将方法的名称及其完整签名插入到代码中。可选参数在注释行中列出。

调用方法时,始终需要关键字 IMPORTING 来接收导出参数的值。但如果您只想提供导入参数,则关键字 EXPORTING 变为可选。

对于只有一个强制导入参数的方法,甚至可以忽略显式参数分配。在此示例中,参数 i_carrier_id 是可选的,缺省值为 'LH'。因此,括号中的值将分配给剩余的强制导入参数 i_connection_id。

如何调用方法

演示观看演示(SAP 官方交互式练习)

异常处理

在本课程的上一部分中,您已了解如何使用 TRY…ENDTRY 控制结构处理运行时错误。

在方法定义的 RAISING 子句中声明的异常的处理方式完全相同:

  • 将方法调用放在 TRY…ENDTRY 控制结构的 TRY 块中。

  • 使用要处理的异常名称定义 CATCH 块。

注意

如果方法调用失败时存在要跳过的代码,则可以将此代码放置在 TRY 块内(在方法调用之后)。

试用:方法调用和异常处理

  1. 与本课程的第一个练习一样,创建一个实施接口 IF_OO_ADT_CLASSRUN 的新全局类。

  2. 将以下代码片段复制到方法 if_oo_adt_classrun~main() 的实施部分:

CONSTANTS c_carrier_id TYPE /dmo/carrier_id       VALUE 'LH'.
CONSTANTS c_connection_id TYPE /dmo/connection_id VALUE '0400'.
DATA connection  TYPE REF TO lcl_connection.
DATA connections  TYPE TABLE OF REF TO lcl_connection.
* Create Instance
**********************************************************************
connection = NEW #(  ).
* Call Method and Handle Exception
**********************************************************************
out->write(  |i_carrier_id    = '{ c_carrier_id }' | ).
out->write(  |i_connection_id = '{ c_connection_id }'| ).
TRY.
connection->set_attributes(
EXPORTING
i_carrier_id    = c_carrier_id
i_connection_id = c_connection_id
).
APPEND connection TO connections.
out->write( `Method call successful` ).
CATCH cx_abap_invalid_value.
out->write( `Method call failed`     ).
ENDTRY.
  1. 导航到 本地类型 标签,然后在此处插入以下代码片段:

CLASS lcl_connection DEFINITION.
PUBLIC SECTION.
* Attributes
DATA carrier_id    TYPE /dmo/carrier_id.
DATA connection_id TYPE /dmo/connection_id.
* Methods
METHODS set_attributes
IMPORTING
i_carrier_id    TYPE /dmo/carrier_id  DEFAULT 'LH'
i_Connection_id TYPE /dmo/connection_id
RAISING
cx_abap_invalid_value.
ENDCLASS.
CLASS lcl_connection IMPLEMENTATION.
METHOD set_attributes.
IF i_carrier_id IS INITIAL OR i_connection_id IS INITIAL.
RAISE EXCEPTION TYPE cx_abap_invalid_value.
ENDIF.
carrier_id    = i_carrier_id.
connection_id = i_connection_id.
ENDMETHOD.
ENDCLASS.
  1. 按 CTRL + F3 激活类,按 F9 将其作为控制台应用执行。

  2. 分析控制台输出。调试程序,使用源代码熟悉概念。特别是,更改两个常量的值以调试异常的引发。

功能性方法

具有返回参数的方法称为函数方法。无法为同一方法定义多个返回参数。必须以 VALUE(<parameter_name>) 形式定义返回参数。除返回参数外,功能方法还可以具有其他参数的任意组合。但是,最常见的是仅添加导入参数。原因是,通过附加导出或更改参数,您将失去功能方法的最大优势,即您可以直接在其他 ABAP 表达式中使用功能方法的结果。

该图显示了如何在表达式中使用函数方法的两个示例;第一个示例是返回参数直接分配给变量的简单示例。第二个示例显示如何将 functional 方法的调用用作其他方法的输入。系统执行功能方法,然后使用返回参数的值作为方法 write( ) 的输入。

试用:功能方法

  1. 与本课程的第一个练习一样,创建一个实施接口 IF_OO_ADT_CLASSRUN 的新全局类。

  2. 将以下代码片段复制到方法 if_oo_adt_classrun~main() 的实施部分:

DATA connection  TYPE REF TO lcl_connection.
DATA connections  TYPE TABLE OF REF TO lcl_connection.
* Create Instance
**********************************************************************
connection = NEW #(  ).
connection->set_attributes(
EXPORTING
i_carrier_id    = 'LH'
i_connection_id = '0400'
).
APPEND connection TO connections.
* Calling Functional Method
**********************************************************************
" in a value assignment (with inline declaration for result)
DATA(result) = connection->get_output( ).
" in logical expression
IF connection->get_output(  ) IS NOT INITIAL.
" as operand in a statement
LOOP AT connection->get_output(  ) INTO DATA(line).
ENDLOOP.
"  to supply input parameter of another method
out->write( data = connection->get_output( )
name = `  ` ).
ENDIF.
  1. 导航到 本地类型 标签,然后在此处插入以下代码片段:

CLASS lcl_connection DEFINITION.
PUBLIC SECTION.
* Attributes
DATA carrier_id    TYPE /dmo/carrier_id.
DATA connection_id TYPE /dmo/connection_id.
* Methods
METHODS set_attributes
IMPORTING
i_carrier_id    TYPE /dmo/carrier_id  DEFAULT 'LH'
i_Connection_id TYPE /dmo/connection_id.
" Functional Method
METHODS get_output
RETURNING VALUE(r_output) TYPE string_table.
*  PROTECTED SECTION.
*  PRIVATE SECTION.
ENDCLASS.
CLASS lcl_connection IMPLEMENTATION.
METHOD set_attributes.
carrier_id    = i_carrier_id.
connection_id = i_connection_id.
ENDMETHOD.
METHOD get_output.
APPEND |------------------------------| TO r_output.
APPEND |Carrier:     { carrier_id    }| TO r_output.
APPEND |Connection:  { connection_id }| TO r_output.
ENDMETHOD.
ENDCLASS.
  1. 按 CTRL + F3 激活类,按 F9 将其作为控制台应用执行。

  2. 分析控制台输出。调试程序,使用源代码熟悉概念。特别是,调试功能方法 get_output() 的不同 调用。

定义和调用方法

在本练习中,您将在本地类 lcl_connection 中定义和实施方法。然后,您将从全局类的 main( ) 方法调用这些方法。

模板:

  • /LRN/CL_S4D400_CLS_INSTANCES(全局类)

解决方案:

  • /LRN/CL_S4D400_CLS_METHODS(全局类)

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

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

步骤

  1. 在 ABAP 编辑器中打开全局类 /LRN/CL_S4D400_CLS_INSTANCES 的源代码。

  2. Eclipse 工具栏中,选择打开 ABAP 开发对象。或者,按 Ctrl + Shift + A

  3. 输入 /LRN/CL_S4D400_CLS 作为搜索字符串。

  4. 从开发对象列表中选择 /LRN/CL_S4D400_CLS_INSTANCES然后选择确定

  5. 将项目浏览器视图与编辑器链接。

  6. 在项目浏览器工具栏中,找到与编辑器链接按钮。如果尚未按下,请选择它。因此,应在项目浏览器中突出显示在编辑器中打开的开发对象。

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

  8. 在项目浏览器视图中,右键单击类 /LRN/CL_S4D400_CLS_INSTANCES 以打开内容菜单。

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

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

  11. 调整描述,然后选择下一步

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

任务 2: 定义方法

在本地类 lcl_connection 中,定义实例方法 get_outputset_attributes

步骤

  1. 导航到本地类 lcl_connection 的定义。

  2. 在全局类中,选择局部类型

  3. 将方法 get_output 添加到 类的公共部分。应具有全局表类型 STRING_TABLE 的一个返回参数 r_output

注意

请记住用 VALUE( ) 括住返回参数的名称。

  1. 如下调整代码:

PUBLIC SECTION.
DATA carrier_id    TYPE /dmo/carrier_id.
DATA connection_id TYPE /DMO/Connection_id.
CLASS-DATA conn_counter TYPE i.
METHODS get_output
returning
value(r_output) type string_table.
PROTECTED SECTION.

注意

此时,忽略方法 "GET_OUTPUT" 缺少语法错误实施。

  1. 将方法 set_attributes 添加到类的公共部分。对于类的每个实例属性,应有一个导入参数。对用于键入属性的参数使用相同类型。要将参数与属性区分开来,请在参数名称中添加前缀 i_ 。此外,该方法应引发异常 CX_ABAP_INVALID_VALUE

  2. 如下调整代码:

PUBLIC SECTION.
DATA carrier_id    TYPE /dmo/carrier_id.
DATA connection_id TYPE /DMO/Connection_id.
CLASS-DATA conn_counter TYPE i.
METHODS set_attributes
IMPORTING
i_carrier_id    TYPE /dmo/carrier_id
i_connection_id TYPE /dmo/connection_id
RAISING
cx_abap_invalid_value.
METHODS get_output
returning
value(r_output) type string_table.
PROTECTED SECTION.

注意

此时,请忽略方法 "SET_ATTRIBUTES" 缺少语法错误实施。

任务 3: 实施方法

步骤

  1. 使用快速修复将方法实施添加到类。

  2. 将光标置于编辑器中其中一种方法的名称上,然后按 Ctrl + 1

  3. 双击建议添加 2 个未实施方法

  4. 实施方法 get_output。附加一些字符串模板以返回参数 r_output。在字符串模板中,使用嵌入的表达式添加属性 carrier_idconnection_id 的值。

  5. 在局部类 中,在语句 METHOD get_output.ENDMETHOD. 之间添加以下代码

APPEND |------------------------------| TO r_output.
APPEND |Carrier:     { carrier_id    }| TO r_output.
APPEND |Connection:  { connection_id }| TO r_output.
  1. 实现方法 set_attributes。如果两个导入参数中的任何一个为空 (IS INITIAL),则引发异常 CX_ABAP_INVALID_VALUE。否则,使用导入参数的值填充两个实例属性。

  2. 如下调整代码:

METHOD set_attributes.
IF i_carrier_id IS INITIAL OR i_connection_id IS INITIAL.
RAISE EXCEPTION TYPE cx_abap_invalid_value.
ENDIF.
carrier_id    = i_carrier_id.
connection_id = i_connection_id.
ENDMETHOD.
  1. 激活类。

  2. 选择 Ctrl + F3 激活类。

任务 4: 调用功能方法

在全局类**的主**方法中,调用功能方法 get_output

步骤

  1. 切换到全局类中方法 if_oo_adt_classrun~main 的实施。

  2. 在全局类中,选择 全局类 向下滚动至方法 if_oo_adt_classrun~main 的实施。

  3. 在 方法的末尾,添加一个通过参考变量**连接作为工作区的内部表连接的**循环。

  4. ENDMETHOD. 之前添加以下代码。

LOOP AT connections INTO connection.
ENDLOOP.
  1. 在 循环中,依次调用每个实例的功能方法 get_output 并将结果写入控制台。

提示

可将方法 get_output 的调用直接用作 out->write() 的输入。

  1. 如下调整代码:

LOOP AT connections INTO connection.
out->write( connection->get_output( ) ).
ENDLOOP.
  1. 激活此类。将其作为控制台应用执行并分析控制台输出。

  2. Ctrl + F3 激活类。

  3. F9 键运行类。

  4. 控制台窗口中分析输出。

任务 5: 使用代码完整化并处理异常

使用代码完整化调用方法 set_attributes 并处理方法引发的异常。

步骤

  1. 对于类 lcl_connection 的第一个实例,使用实例方法 set_attributes**()** 的调用替换对属性 carrier_idconnection_id 的直接访问。使用代码完整化插入方法的完整签名。

提示

键入组件后按 Ctrl + 空格键 选择 (->) 以显示可用属性和方法的列表。选择方法,然后按 Shift + Enter 插入方法的完整签名。

  1. 将光标放在第一个 connection = NEW #( ). 之后的下一行中。

  2. 键入 connection-> 并按 **Ctrl + 空格**键。

  3. 选择 set_attributes 并按 Shift + Enter

  4. 将值传递到导入参数。使用之前用于设置此实例属性的相同文字。

  5. 移除或注释对此实例的实例属性的直接访问。

  6. 处理异常。将方法调用与 TRY.ENDTRY. 括起来。添加 CATCH-Block 以处理异常 CX_ABAP_INVALID_VALUE。确保仅当方法调用成功时,才将新实例添加到内部表**连接**。

  7. 取消注释生成的代码行 CATCH cx_abap_invalid_value.

  8. 在方法调用之前添加 TRY.

  9. 在 CATCH 语句后添加 ENDTRY.

  10. CATCH 语句和 ENDTRY 语句之间,添加 out->write( Method call failed ).

  11. 将 APPEND 语句向上移动到方法调用和 CATCH 语句之间。

  12. 完整的方法调用现在应如下所示:

TRY.
connection->set_attributes(
EXPORTING
i_carrier_id    = 'LH'
i_connection_id = '0400'
).
*       connection->carrier_id    = 'LH'.
*       connection->connection_id = '0400'.
APPEND connection TO connections.
CATCH cx_abap_invalid_value.
out->write( `Method call failed` ).
ENDTRY.
  1. 对类 lcl_connection 的其它两个实例重复上述步骤。

  2. 完成此步骤后,方法 if_oo_adt_classrun~main 的实施应如下所示:

METHOD if_oo_adt_classrun~main.
DATA connection TYPE REF TO lcl_connection.
DATA connections TYPE TABLE OF REF TO lcl_connection.
* First Instance
**********************************************************************
connection = NEW #(  ).
TRY.
connection->set_attributes(
EXPORTING
i_carrier_id    = 'LH'
i_connection_id = '0400'
).
*        connection->carrier_id    = 'LH'.
*        connection->connection_id = '0400'.
APPEND connection TO connections.
CATCH cx_abap_invalid_value.
out->write( `Method call failed` ).
ENDTRY.
* Second instance
**********************************************************************
connection = NEW #(  ).
TRY.
connection->set_attributes(
EXPORTING
i_carrier_id    = 'AA'
i_connection_id = '0017'
).
*        connection->carrier_id    = 'AA'.
*        connection->connection_id = '0017'.
APPEND connection TO connections.
CATCH cx_abap_invalid_value.
out->write( `Method call failed` ).
ENDTRY.
* Third instance
**********************************************************************
connection = NEW #(  ).
TRY.
connection->set_attributes(
EXPORTING
i_carrier_id    = 'SQ'
i_connection_id = '0001'
).
*        connection->carrier_id    = 'SQ'.
*        connection->connection_id = '0001'.
APPEND connection TO connections.
CATCH cx_abap_invalid_value.
out->write( `Method call failed` ).
ENDTRY.
* Output
**********************************************************************
LOOP AT connections INTO connection.
out->write( connection->get_output( ) ).
ENDLOOP.
ENDMETHOD.
  1. 激活此类。执行并调试方法调用。

  2. Ctrl + F3 激活类。

  3. F9 键运行类。