OO语法基础 练习

OO语法基础 练习

#practice #oo-syntax #abap-objects

📌核心模式
关键词 答案
类定义 CLASS … DEFINITION / CLASS … IMPLEMENTATION
可见性 PUBLIC SECTION, PROTECTED SECTION, PRIVATE SECTION
构造函数 CONSTRUCTOR 方法,在 CREATE OBJECT 时自动调用
引用变量 TYPE REF TO,指向对象的引用

题目1 - [记忆] 类定义的基本结构

请写出ABAP中定义和实现一个类的完整语法框架。

📌查看答案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
" 类定义(声明)
CLASS lcl_example DEFINITION.
PUBLIC SECTION.
" 公有属性和方法声明
PROTECTED SECTION.
" 受保护的属性和方法声明
PRIVATE SECTION.
" 私有属性和方法声明
ENDCLASS.

" 类实现
CLASS lcl_example IMPLEMENTATION.
METHOD method_name.
" 方法实现代码
ENDMETHOD.
ENDCLASS.

关键点:

  • DEFINITION 部分声明类的接口(属性、方法、事件)
  • IMPLEMENTATION 部分实现所有在定义中声明的方法
  • 类名约定:局部类以 LCL_ 开头,全局类以 CL_ 开头

题目2 - [记忆] 三种可见性区域

请解释ABAP类中 PUBLIC SECTION、PROTECTED SECTION 和 PRIVATE SECTION 的区别。

📌查看答案
可见性 访问范围 用途
PUBLIC SECTION 所有外部程序、子类和类本身均可访问 定义外部接口,提供公共服务
PROTECTED SECTION 仅限类本身及其子类(继承者)访问 为子类提供可复用的内部方法和属性
PRIVATE SECTION 仅限类本身访问,子类也无法直接访问 隐藏内部实现细节,保护数据完整性

设计原则:

  • 属性通常设为 PRIVATE,通过公有方法提供受控访问(getter/setter)
  • 供子类重定义的方法放在 PROTECTED SECTION
  • 对外提供的服务接口放在 PUBLIC SECTION
  • 遵循最小可见性原则:尽可能使用最严格的可见性

题目3 - [记忆] 实例属性与静态属性

请说明实例属性(Instance Attribute)和静态属性(Static Attribute)的区别。

📌查看答案
特性 实例属性 静态属性
声明 DATA: attr TYPE type CLASS-DATA: attr TYPE type
存在方式 每个对象实例各有一份独立的副本 整个类只有一份,所有实例共享
访问方式 通过对象引用访问:oref->attr 通过类名访问:lcl_class=>attr,也可通过引用访问
生命周期 随对象的创建和销毁 类被加载时创建,程序运行期间一直存在
典型用途 存储对象的状态数据(如订单金额) 计数器、配置信息、共享常量

示例:

1
2
3
4
5
CLASS lcl_counter DEFINITION.
PUBLIC SECTION.
DATA: count TYPE i. " 实例属性
CLASS-DATA: total TYPE i. " 静态属性
ENDCLASS.

题目4 - [记忆] 构造函数的作用和特点

请说明ABAP中构造函数(CONSTRUCTOR)的特点和用途。

📌查看答案
构造函数的特点:
  1. 名称固定:必须命名为 CONSTRUCTOR(实例构造函数)或 CLASS_CONSTRUCTOR(静态构造函数)
  2. 自动调用:在使用 CREATE OBJECT 创建对象时自动被调用
  3. 初始化:用于设置对象的初始状态,初始化属性值
  4. 参数支持:可以定义 IMPORTING 参数和异常,在创建对象时传入初始值
  5. 不可继承:每个类必须定义自己的构造函数(但子类构造函数会隐式调用父类构造函数)

语法:

1
2
3
4
METHODS: constructor IMPORTING iv_name TYPE string.

" 创建对象时传入参数
CREATE OBJECT lo_obj EXPORTING iv_name = 'Test'.

静态构造函数(CLASS_CONSTRUCTOR):在类首次被访问时自动调用一次,仅用于初始化静态属性,不能有参数。


题目5 - [记忆] 引用变量和对象创建

请说明ABAP中如何声明引用变量、创建对象以及访问对象的属性和方法。

📌查看答案

声明引用变量

1
DATA: lo_customer TYPE REF TO lcl_customer.  " 声明引用变量

创建对象

1
2
3
4
5
CREATE OBJECT lo_customer.                    " 创建对象实例
" 或带参数:
CREATE OBJECT lo_customer
EXPORTING
iv_name = 'John'.

NEW 操作符(内联创建)

1
DATA(lo_customer) = NEW lcl_customer( iv_name = 'John' ).

访问属性和方法

1
2
3
4
5
6
" 实例成员使用 -> (对象组件选择器)
lo_customer->set_name( 'John' ).
DATA(lv_name) = lo_customer->get_name( ).

" 静态成员使用 => (类组件选择器)
DATA(lv_count) = lcl_customer=>instance_count.

关键概念:引用变量存储的是指向对象的指针(引用),而非对象本身。


题目6 - [记忆] 方法定义的参数类型

请列出ABAP方法中可以使用的四种参数类型(参数传递方式)及其特点。

📌查看答案
参数类型 关键字 特点
导入参数 IMPORTING 从调用者传入方法,方法内只读
导出参数 EXPORTING 从方法返回给调用者,方法内写入
变更参数 CHANGING 从调用者传入并可能被方法修改,可读写
返回参数 RETURNING 返回单个值,支持函数式调用链式写法

语法示例:

1
2
3
4
5
METHODS: calculate_price
IMPORTING iv_quantity TYPE i
iv_price TYPE p
EXPORTING ev_total TYPE p
RETURNING VALUE(rv_result) TYPE p.

注意:

  • RETURNING 参数只能有一个,且不能与 EXPORTINGCHANGING 同时使用
  • RETURNING 参数使用 VALUE() 语法,表示按值传递
  • 推荐使用 RETURNING 以支持函数式编程风格

题目7 - [应用] 编写类定义

请根据以下需求编写一个ABAP类的完整定义和实现:

  • 类名:lcl_product(商品类)
  • 有私有的商品名称和价格属性
  • 构造函数接收名称和价格参数进行初始化
  • 公有方法 get_price_with_tax 返回含税价格(税率10%)
📌查看答案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
CLASS lcl_product DEFINITION.
PUBLIC SECTION.
METHODS:
constructor IMPORTING iv_name TYPE string
iv_price TYPE p DECIMALS 2,
get_price_with_tax RETURNING VALUE(rv_price) TYPE p DECIMALS 2.
PRIVATE SECTION.
DATA:
mv_name TYPE string,
mv_price TYPE p DECIMALS 2.
ENDCLASS.

CLASS lcl_product IMPLEMENTATION.
METHOD constructor.
mv_name = iv_name.
mv_price = iv_price.
ENDMETHOD.

METHOD get_price_with_tax.
rv_price = mv_price * '1.10'.
ENDMETHOD.
ENDCLASS.

使用示例:

1
2
3
DATA(lo_product) = NEW lcl_product( iv_name = '笔记本电脑' iv_price = '5999.00' ).
DATA(lv_tax_price) = lo_product->get_price_with_tax( ).
" lv_tax_price = 6598.90

题目8 - [应用] 方法调用分析

分析以下代码的执行结果。请逐步追踪 lo_ref 引用变量的变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
CLASS lcl_demo DEFINITION.
PUBLIC SECTION.
METHODS: set_value IMPORTING iv_val TYPE i.
METHODS: get_value RETURNING VALUE(rv_val) TYPE i.
PRIVATE SECTION.
DATA: mv_value TYPE i.
ENDCLASS.

CLASS lcl_demo IMPLEMENTATION.
METHOD set_value. mv_value = iv_val. ENDMETHOD.
METHOD get_value. rv_val = mv_value. ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
DATA: lo_ref1 TYPE REF TO lcl_demo.
DATA: lo_ref2 TYPE REF TO lcl_demo.

CREATE OBJECT lo_ref1.
lo_ref1->set_value( 10 ).

lo_ref2 = lo_ref1.
lo_ref2->set_value( 20 ).

WRITE: / lo_ref1->get_value( ).
📌查看答案
输出结果:20

逐步分析:

  1. CREATE OBJECT lo_ref1 — 创建一个 lcl_demo 对象,lo_ref1 指向该对象
  2. lo_ref1->set_value( 10 ) — 将该对象的 mv_value 设置为 10
  3. lo_ref2 = lo_ref1关键步骤:将 lo_ref1 的引用赋值给 lo_ref2,此时 lo_ref1lo_ref2 指向同一个对象(不是创建新对象)
  4. lo_ref2->set_value( 20 ) — 修改了同一对象的 mv_value 为 20
  5. lo_ref1->get_value( ) — 返回该对象的 mv_value,此时值为 20

核心概念:引用赋值(lo_ref2 = lo_ref1)不创建新对象,而是让两个引用变量指向同一块内存。通过任一引用修改对象,另一个引用也能看到变化。


题目9 - [应用] 静态成员使用场景

以下代码试图实现一个对象计数器。请找出代码中的错误并修正。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CLASS lcl_counter DEFINITION.
PUBLIC SECTION.
METHODS: constructor.
DATA: mv_count TYPE i.
PRIVATE SECTION.
CLASS-DATA: gv_instance_count TYPE i.
ENDCLASS.

CLASS lcl_counter IMPLEMENTATION.
METHOD constructor.
gv_instance_count = gv_instance_count + 1.
mv_count = gv_instance_count.
ENDMETHOD.
ENDCLASS.
📌查看答案
代码中的问题:
  1. mv_count 可见性不当mv_count 被定义在 PUBLIC SECTION,外部代码可以随意修改它,破坏了计数器的封装性。应移到 PRIVATE SECTION
  2. 设计问题mv_count(实例属性)在构造函数中被赋值为当前实例编号,但外部可以随意修改。如果目的是为每个实例分配唯一编号,应该通过公有方法提供只读访问

修正版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
CLASS lcl_counter DEFINITION.
PUBLIC SECTION.
METHODS:
constructor,
get_instance_number RETURNING VALUE(rv_num) TYPE i,
get_total_instances RETURNING VALUE(rv_count) TYPE i.
PRIVATE SECTION.
DATA: mv_instance_number TYPE i.
CLASS-DATA: gv_instance_count TYPE i.
ENDCLASS.

CLASS lcl_counter IMPLEMENTATION.
METHOD constructor.
gv_instance_count = gv_instance_count + 1.
mv_instance_number = gv_instance_count.
ENDMETHOD.

METHOD get_instance_number.
rv_num = mv_instance_number.
ENDMETHOD.

METHOD get_total_instances.
rv_count = gv_instance_count.
ENDMETHOD.
ENDCLASS.

题目10 - [分析] 方法参数传递方式选择

请分析以下三种方法签名,讨论它们的适用场景以及哪种方式是推荐的最佳实践。

1
2
3
4
5
6
7
8
9
10
11
" 方式A
METHODS: get_full_name
EXPORTING ev_name TYPE string.

" 方式B
METHODS: get_full_name
CHANGING cv_name TYPE string.

" 方式C
METHODS: get_full_name
RETURNING VALUE(rv_name) TYPE string.
📌查看答案
三种方式的对比分析:
特性 方式A (EXPORTING) 方式B (CHANGING) 方式C (RETURNING)
语义 方法输出结果给调用者 方法修改调用者传入的变量 方法返回一个值
调用方式 需要先声明接收变量 需要先初始化传入变量 可直接内联使用
链式调用 不支持 不支持 支持
参数数量 可有多个 可有多个 仅限一个

推荐:方式C(RETURNING)是最佳实践

理由:

  1. 函数式风格:支持链式调用和内联表达式
    1
    2
    " RETURNING 支持这种写法
    lo_person->get_full_name( )->to_upper( ).
  2. 意图清晰:明确表示方法返回一个值,不修改任何已有变量
  3. 代码简洁:无需预先声明接收变量
    1
    2
    3
    4
    5
    6
    " RETURNING
    DATA(lv_name) = lo_person->get_full_name( ).

    " EXPORTING 需要更多代码
    DATA: lv_name TYPE string.
    lo_person->get_full_name( IMPORTING ev_name = lv_name ).

何时用其他方式

  • EXPORTING:方法需要返回多个值时
  • CHANGING:方法需要读取并修改调用者的变量时
  • 一般规则:返回单个值用 RETURNING,返回多个值用 EXPORTING

📌模式总结
关键词 答案
类定义结构 DEFINITION(声明)+ IMPLEMENTATION(实现)
可见性三级 PUBLIC(所有)> PROTECTED(类+子类)> PRIVATE(仅类本身)
实例 vs 静态 DATA(每对象一份)vs CLASS-DATA(全类共享)
构造函数 CONSTRUCTOR自动调用,初始化对象状态
引用赋值 赋值引用不创建新对象,两个引用指向同一实例
参数传递 RETURNING推荐(支持链式),EXPORTING用于多返回值
组件选择器 -> 实例成员,=> 静态成员