一、面向对象
面向对象(Object-Oriented, OO)是现代编程的一种思想和方法,它通过模拟现实世界中的对象及其相互作用来组织和设计程序。ABAP中的面向对象和JAVA、C++等类似,有面向对象的语言开发基础的理解起来会更加快速。有关的事务码:SE24(全局定义) ,SE38(在代码中定义)。面向对象的核心概念包括以下几个主要方面:
a、类和对象:类定义了对象的属性和行为,对象是类的实例。
b、封装:将数据和方法封装在一起,隐藏实现细节。
c、继承:通过继承实现代码复用和扩展。
d、多态:同一接口调用不同对象的不同实现。
e、抽象:通过抽象类或接口简化复杂系统的设计。
二、类和类的属性和方法
类:类是对象的模板或蓝图,定义了对象的属性(数据)和行为(方法)。类可以看作是对一类事物的抽象描述。
a、属性:类中的变量,用来表示对象的状态或特征。
静态属性 :静态属性(Static Attributes) ------较为特殊的属性,是属于类 而不是类的实例的成员变量。静态属性在所有类的实例中共享,即所有对象实例访问的是同一份静态数据,而不是各自独立的一份。静态属性通常用于存储类级别的数据,而非对象实例级别的数据。可以更改,但是一改所有的实例的该属性全部更改。
b、方法:类中的函数,用来表示对象的行为或功能。其中最基础的方法就是------构造函数和析构函数。
1)构造函数 :构造函数(Constructor)是一个特殊的方法,用于在对象创建时 初始化对象的属性或执行一些必要的初始化工作。构造函数通常会在类的实例化过程中自动调用。可以不显式定义出来,一般只在需要初始化数据的时候进行显式定义。但如果显式定义,则在创建对象的时候需要EXPORTING参数。(可重载?)ABAP中类的构造函数就叫constructor。所以查其他类的构造函数时可以直接搜constructor。
2)析构函数 :在ABAP中,类是可以定义析构函数(Destructor)的。析构函数在类的实例销毁时自动调用,用于清理资源。在ABAP中,垃圾回收会在不再使用对象时自动释放内存,因此析构函数主要用于处理非内存资源的清理,如关闭数据库连接、释放外部资源等。一般不用。
c、一般的定义
类中至少要有属性/方法的定义,否则类没有意义(抽象类就算不实现也进行了定义)。定义类的时候的方法只进行定义但不进行具体的实现,会在后续进行实现。**注意这里的构造函数一般只在需要初始化的时候才定义和实现。**一般的代码模式如下:
CLASS lcl_classname DEFINITION."方法只进行定义,不进行实现
(PUBLIC SECTION.
DATA:公开的属性...
METHODS:公开的方法...)
(PROTECTED SECTION.
DATA:保护的属性...
METHODS:保护的方法...)
(PRIVATE SECTION.
DATA:私有的属性...
METHODS:私有的方法...)
ENDCLASS.
CLASS lcl_classname IMPLEMENTATION."类方法的具体实现
(METHOD constructor."构造函数的实现,需要初始化才写,不用不用写
具体实现...
ENDMETHOD.)
(METHOD method_name1."方法的实现
具体实现...
ENDMETHOD.)
(METHOD method_name2."方法的实现
具体实现...
ENDMETHOD.)
...
ENDCLASS.
三、封装
封装(Encapsulation) 是面向对象编程的一个重要概念,它通过将属性和方法行为绑定在一起,并且通过访问控制来隐藏对象的内部实现细节,从而提供清晰的接口和更好的数据保护。体现在类与类之间的访问和继承中。将与继承一起进行讨论。注意对于一个类自己而言,所有的属性和方法都是全开的。
访问控制分为:
a、公开(PUBLIC)公共部分,外部可以访问。
b、保护(PROTECTED)受保护部分,只有类本身和子类可以访问。
c、私有(PRIVATE)私有部分,只有类本身可以访问。
四、类的继承
继承是面向对象编程的一种机制,它允许创建一个新的类(子类),从一个现有的类(父类)继承属性和方法 。通过继承,子类可以复用父类的代码,并且可以根据需要扩展或修改 父类的行为。ABAP中子类只可以继承父类的PUBLIC和PROTECTED的属性和方法。 这里通过SE38在程序里面写,在抽象部分提到接口和抽象类的时候建立全局的类(一般可以创建全局的时候都建议创建全局数据,包括表结构和结构体结构等,提高可复用性且便于后续更改和管理数据)。
a、编写父类的代码
这个代码简单到有点像JavaBean了,可以自己试着写一下更复杂的逻辑。主要就是给数和取数。
REPORT ZVIA_11_25_CSDN.
CLASS lcl_father DEFINITION."做父类
PUBLIC SECTION."公开的区域,属性和方法为所有人可见
DATA : "定义实例(OBJECT)所有的属性,每个实例都有自己独特的值
gv_father_public TYPE c LENGTH 20 VALUE '父类public初始值'.
CLASS-DATA : "静态属性:父类的属性 所有的实例的gv_static都是'static_data' 没有个体差异的属性可用CLASS-DATA来定义
gv_father_static TYPE string VALUE 'static_data'.
METHODS : "方法,公开的
constructor IMPORTING iv_father_id TYPE i,"构造函数 如果需要初始化那么显式定义
set_father_public IMPORTING iv_father_public_input TYPE string,
get_father_public RETURNING VALUE(rv_father_public_return) TYPE string.
PROTECTED SECTION."受保护的地方,自己和子类可见
DATA :
gv_father_protected TYPE string VALUE '这是父类的被保护的数据'.
METHODS:
get_father_protected RETURNING VALUE(rv_father_protected_return) TYPE string.
PRIVATE SECTION."私有,仅自己可见
DATA :
gv_father_id TYPE i,
gv_father_private TYPE i.
METHODS :
add_father_private IMPORTING iv_father_private_input TYPE i,"增加私有
get_father_private RETURNING VALUE(rv_father_private_return) TYPE i."获取私有
ENDCLASS.
CLASS lcl_father IMPLEMENTATION."类的实现,具体方法实现
"构造函数会在创建类的对象的时候自动调用
METHOD constructor."写构造函数的实现,如果需要用到就写,不要用不用写 一般只有需要进行初始化的时候才用
gv_father_id = iv_father_id."给私有的属性id赋值
WRITE : / '构造函数已被调用'.
ENDMETHOD.
METHOD set_father_public.
WRITE : / '父类set_father_public函数已被调用'.
gv_father_public = iv_father_public_input.
ENDMETHOD.
METHOD get_father_public.
WRITE : / '父类get_father_public函数已被调用'.
rv_father_public_return = gv_father_public.
ENDMETHOD.
METHOD get_father_protected.
WRITE : / '父类get_father_protected函数已被调用'.
rv_father_protected_return = gv_father_protected.
ENDMETHOD.
METHOD add_father_private.
WRITE : / '父类add_father_private函数已被调用'.
gv_father_private = gv_father_private + iv_father_private_input.
ENDMETHOD.
METHOD get_father_private.
WRITE : / '父类get_father_private函数已被调用'.
rv_father_private_return = gv_father_private.
ENDMETHOD.
ENDCLASS.
b、编写子类的代码。
CLASS lcl_son DEFINITION INHERITING FROM lcl_father.
类的定义...
ENDCLASS.
* 父类的public和protected的属性/方法都继承,除了private的 *
* 可以继续添加属于自己的属性 *
CLASS lcl_son DEFINITION INHERITING FROM lcl_father."定义一个子类继承之前定义的父类
PUBLIC SECTION.
DATA :
gv_son_public TYPE c LENGTH 8.
METHODS :
get_son_public RETURNING VALUE(rv_son_public) TYPE string.
PROTECTED SECTION.
DATA :
gv_son_protected TYPE string VALUE '这是子类的被保护的数据'.
METHODS :
get_father_protected REDEFINITION."对父类中的方法进行重定义
ENDCLASS.
* 子类自己定义的方法/重定义父类方法的实现 *
CLASS lcl_son IMPLEMENTATION.
METHOD get_son_public.
WRITE : / '子类set_father_public函数已被调用'.
ENDMETHOD.
METHOD get_father_protected.
* 用关键字SUPER调用父类的方法 *
WRITE : / '子类重定义父类的函数get_father_protected已被调用'.
ENDMETHOD.
ENDCLASS.
c、尝试调取数据
1)分别定义一个父类和子类的对象。
这里在定义的时候都传了一个iv_father_id的参数------因为在父类里面显式定义了构造函数并需要传入参数,所以在创建对象的时候必须给构造函数传入参数。
START-OF-SELECTION.
* 分为 定义+创建两步 具体的区别在多态演示 *
WRITE : / '父类对象定义:'.
DATA lo_f TYPE REF TO lcl_father."定义,用父类来定义
CREATE OBJECT lo_f
EXPORTING iv_father_id = 1."创建对象 实例化
WRITE : / '子类对象定义:'.
DATA lo_s TYPE REF TO lcl_son."定义,用子类来定义
CREATE OBJECT lo_s
EXPORTING iv_father_id = 2."创建对象 实例化
输出如下,子类继承了父类定义的构造函数,因此也会输出"构造函数已被调用"。
2)父类对象调用自己的属性。
2-1)访问共用的属性。
对象名->属性名
* 父类对象调用父类的属性 *
ULINE.
WRITE : / lo_f->gv_father_public.
2-2)访问被保护/私有的属性。直接报错,不可以。
* 父类对象调用父类的属性 *
ULINE.
WRITE : / lo_f->gv_father_protected.
2-3)访问静态属性,可以用上面的方法对象名->属性名(可能存在不可以的情况,但是博主的系统是可以的)。 也可以用类名=>静态属性来访问。(所有以这个类来定义的都是同样的值,所以可以用类来访问)。
* 用对象名->静态属性 调用静态属性 *
WRITE : / '用对象名->静态属性 调用静态属性:',lo_f->gv_father_static.
* 用类名=>静态属性 调用静态属性 *
WRITE : / '用类名=>静态属性 调用静态属性:',lcl_father=>gv_father_static.
3)父类对象调用自己的方法。
3-1)调用公有方法
对象名->方法名(参数).(若没有参数括号之间要留有空格)
这里如果在定义的时候用的EXPORTING调用时用IMPORTING,定义的时候用IMPORTING,调用时用EXPORTING。
* 父类对象调用父类方法 *
ULINE.
lo_f->set_father_public(
EXPORTING iv_father_public_input = 'text'
).
ULINE.
WRITE : / '父类public方法(有返回值):',lo_f->get_father_public( ).
输出如下,存在返回值,所以可以直接write出来。
3-1)调用被保护/私有方法,不被允许,直接报错。
4) 子类对象调用父类的方法。
4-1)子类调用父类的共有方法。
子类对象名->父类方法名(参数).
* 子类对象调用父类共有方法 *
ULINE.
WRITE : / '子类对象调用父类的public方法(有返回值):',lo_s->get_father_public( ).
4-2)子类调用父类的被保护方法。
* 子类对象调用父类共有方法 *
ULINE.
WRITE : / '子类对象调用父类的private方法:',lo_s->get_father_protected( ).
4-3)子类调用父类的私有方法。
* 子类对象调用父类私有方法 *
ULINE.
WRITE : / '子类对象调用父类的private方法:',lo_s->get_father_private( ).
由于根本不能继承父类的私有方法,所以这里显示不存在。
5)父类调用子类特有的属性和方法。
不可以!
五、多态
多态(Polymorphism) 是面向对象编程的重要特性之一,允许不同的类在继承关系中使用相同的方法名来执行不同的行为。它能够让你在运行时决定调用哪个方法,从而使得代码更具灵活性和可扩展性。
ABAP 支持两种类型的多态:
1、静态多态(静态方法重载)
2、动态多态(动态方法重写,最常用)
实现和接口一起看。
六、抽象
通过接口或抽象类实现。
抽象类(Abstract Class) 是一种特殊的类,它不能被实例化,必须通过继承来实现其功能。抽象类通常用于定义一组方法的框架或接口,供其他类继承并提供具体的实现。这种机制有助于实现 多态性 和 代码重用 。如果一个类继承了一个抽象类但是没有实现方法,也会变成抽象类。
接口(Interface) 是一种定义行为契约的机制,通常用于确保类遵循某些约定,提供特定的方法,但不包含实现的具体细节。接口定义了一组方法(签名),但不提供方法的实现,类需要通过实现这些方法来履行接口所规定的合同。具体说接口。
1、通过事务码SE24来创建全局可使用的接口/类。
a、填写接口名,点击创建。以ZIF开头会自动识别要建的是接口。
b、在属性处写接口的属性。
c、在方法处写接口的方法。接口的方法不需要进行实现,就是个声明。
d、其他需要补充的自行补充,保存后激活就行。
2、创建使用该接口的类。
a、填写类名,点击创建。以ZCL开头会自动识别要建的是类。
b、 填写信息。点击保存。
c、在接口处填写接口。
d、查看属性、方法,可以发现继承了接口中定义的属性和方法。可以自己添加属性和方法。但是由于接口中的方法没有实现,所以这里需要编写方法的实现代码。
写参数,类型如图。
e、编写方法代码。
1)编写从接口中定义的方法。
方法名和属性名都需要用接口名~属性名/方法名的形式。
2)编写类中新定义的方法。
接口中定义的属性都需要接口名~方法名。方法自己定义的参数直接写参数名就行。
3)创建子类,步骤相同,只是要填写超类。
点击超类,填写父类名。
4)需要添加属性和方法的自行添加。完成后别忘记保存激活。在程序中的使用和上面具体的在程序中定义的类方法相同。
3、涉及到接口的多态。
a、用接口定义对象,用子类来创建对象。
*多态*
*用接口定义,用子类创建*
*只可以用接口和子类里面都有的方法,具体的实现是看子类里面有没有重定义,按照子类的来*
DATA : lo_interface TYPE REF TO zif_csdn_person."用接口定义对象
CREATE OBJECT lo_interface TYPE zcl_csdn_student."用子类来创建对象
子类中的方法。
b、使用接口中定义的方法(子类中已经实现了)。
lo_interface->set_id("可以直接用接口里面的方法,不用接口名~方法了
EXPORTING
iv_id = 10
).
WRITE : '使用接口中定义的类中实现的方法:',lo_interface->get_id( ).
c、用子类中定义的方法。
不可以用子类中定义的方法,因为创建对象的时候是用接口定义的,接口中没有该方法,不能够找到定义。
lo_interface->set_score("但是不能用子类里面定义的方法 会报错
EXPORTING
iv_score = 100
).
4、通过接口进行多态的总结。
a、谁定义可以用谁的方法。但是用于创建的也必须存在该方法并实现了。
b、最后的结果由用于创建的类的方法来决定。
c、即方法必须存在用于定义的接口中,具体实现结果看用于创建的类是怎么实现的。