文章目录
8. ABAP 面向对象编程
8.1 与面向过程编程的区别
待总结,刚看完面向过程ABAP,个人感觉像C语言中的主函数调用子函数。
面向对象呢,就是封装啊,继承啊,一些比较复杂的概念。
回头补。
8.2 回顾ABAP变量和数据类型
数据类型:就是一个变量装啥样的数据。
变量:声明了一个变量,它就会指向内存一块地方来保留数据的值。就是说我要一块地方。
在变量声明过程中指定的数据类型,操作系统会分配内存,决定怎么在内存中来保存这个数据。
插播: 字和字节的区别
bit: binary digit(二进制数)的简写,要么是1,要么是0,是指最小的数据单位。就是个有电没电的电路信号。
byte:一字节等于8bit。为啥?一开始美国人搞的ASCII码,127位就囊括了所有数字,字母和特殊字符。对于美国人来讲,这个ASCII码的127位就用8个bit就够了。所以byte作为内存或者外部存储的最小单位。(后来有扩展)
1MB = 8Mb
bit 一般用来衡量速度:Mbps
这个意思是,一秒内把多少位字从一个设备给移到另一个设备。宽带10Mbps就是这个意思。一秒就有这么多bit到你手机上。为啥用bit来衡量速度,不用byte? 你发送出去的数据,比如邮件一般都是被分解成数据包,然后从不同方向发送,最后到达别人的接收处是顺序混乱需要重排的。所以呢,这种不稳定的数据流不好划分字节,那就用比特衡量。
byte 用来衡量大小:MB
相对于内存来说,1 byte是最小存储单元。硬盘,U盘和系统内存一般用这个单位。软件大小也按这个来衡量。
那么比如HI这个单词,就需要两个字节。Hello就需要5个字节。
插播结束
现在回到ABAP的数据类型。
基本数据类型就有以下这么多。有些是定长的,有些是不定长的。
比如说定义一个I整型的,那系统就会指定4个字节空间给你,那么你的这个数字最大就是± 2的31次方。
对于一个字符的,我觉得也是1byte。只不过对于字符型的,直接用字符作为大小。65535个byte。
还有些可以参照这里:别人写的.
接下篇: 在ABAP对象中使用内表.
以下内容在下篇:ABAP 面向对象编程-2.
8.3 面向对象中的概念
8.3.1 继承
就是说类和别人分享自己的结构和方法
8.3.2 多态
共用一个沟通接口的不同对象
8.3.3 事件
用来对对象作出反应及触发事件
8.4 类
类要定义后实现。
类似于C语言要定义类的对象和方法。同时对象也有公有私有。
在ABAP中定义类,对象就是attribute,还有types,interface可以定义。等等。
8.4.1 类的定义
类分为全局类和局部类。
全局类和接口在 SE24构建。最终存储在SAP Class Reposistory的class Pools里面。所有的ABAP程序都可以访问全局类。
局部类就是本地类,在ABAP程序内部自己定义,也就是说SE38弄。
那么其实大家最好用全局类。默认用Y或Z开头的。局部类么,你就随便用啥开头。
8.4.1.1 局部类
就是纯的搞代码。定义和实现。
定义就是名称,属性,方法。
实现就是方法的代码。
CLASS user DEFINITION.
PUBLIC SECTION.
DATA: name TYPE c LENGTH 30,
age TYPE i,
gender TYPE c LENGTH 1 READ-ONLY.
CLASS-DATA : count TYPE i.
PRIVATE SECTION.
DATA : loginid TYPE c LENGTH 20,
pwd TYPE c LENGTH 15.
ENDCLASS.
类的定义中还有方法:
作为一个BW工作者,经常在转换里面看到:
methods GLOBAL_END
importing
!REQUEST type RSREQUEST
!DATAPACKID type RSDATAPID
!SEGID type RSBK_SEGID
exporting
!MONITOR type RSTR_TY_T_MONITORS
changing
!RESULT_PACKAGE type _ty_t_TG_1
raising
CX_RSROUT_ABORT
CX_RSBK_ERRORCOUNT .
methods NEW_RECORD__END_ROUTINE
importing
!SOURCE_SEGID type RSTRAN_SEGID default 0001
!SOURCE_RECORD type SYTABIX
exporting
!RECORD_NEW type SYTABIX .
在类的方法定义中。
Import: 就是传给这个方法的值。
export:就是从方法中传出去的值。
changing:就是两者皆有,传入到方法的值,最后更改后再传出。
raising:啥例外。
注意,方法的定义也是在类中的,实际上endclass应该在方法定义结束之后。
8.4.1.2 属性
就是类中的数据字段,对象。在类声明的时候声明。在不同的位置,比如public section,private section。私有的就是说,只有我类里面的组件能访问,比如类里面的方法。类之外的不行。
属性分为对象实例和静态属性。
对象实例用DATA声明。上面的例子里有。
(20220411这个现在来看又有些不理解了,对象作为一种特殊的变量,也是要先声明一下的,就是data 对象名 type ref to 类名。这个其实是声明一个对象类型,然后当然还要实例化对象,就是create object 对象名,实例化了之后,你去看代码其实是给这个实例化的对象分配了一个和类一样的,有类的所有属性和方法的空间了。type ref to我理解就是指针指向类,这具体以后再理解)
静态属性用CLASS-DATA声明。如果静态属性改了,大家都能看到。所以这个家伙在public区。
8.4.1.3 方法
类中的对象的行为。方法可以访问类的属性。就是说它定义的对象空间。方法里面可以包含参数。当你去call这个method的时候,就传入数值给这个方法。方法的声明在类的声明里,实现在类的实现里。METHOD…ENDMETHOD。
如果是静态方法也是一样的用CLASS-METHOD。
不同的就是静态方法调用的时候是用以下语法。
实际上类里定义的属性,方法等等,如果光定义,那它是不会有啥执行的,那咋办,所以要创建对象将对象实例化了之后,再去调用类的方法。这时也就是说我对象是实例化了的,我可以有空间去执行方法的。也可以填值进入那些属性的。
call method classname=>methodname
实例方法的调用如下:如果只import一个参数,那就把参数放在圆括号里,括号前没有空格,括号后有空格。
classname->methodname( data )
如果方法里不止import一个参数,那就用形参=实参成对的把实参赋值给形参。
classname->methodname( param1 = data1 param2 = data2 ).
如果不是只import参数进去,那就把参数的类型也带上:
注意这里export和import是反着来的。
因为你是call别人的方法。就是说我在这里call一个方法去干活。就是我叫一个小弟去干活。那我要export一个指令给他去。到他那边呢,他就接了也就是import了这个指令。等他除了完了export出来然后我再接进来,就是import进来。
如果你call的那个method是returning, 我们这边call的就是receiving。
calssname->methodname( EXPORTING param1 = data1 param2 = data2
IMPORTING param3 = data3 param4 = data4
CHANGING param5 = data5 param6 = data6).
懂了这些我觉得我一个搞BW的差不多能看懂代码了。
说实话我看到这里我觉得对我来说又是知识盲区了。我觉得我得去把C语言捡起来了。
REPORT ztest.
CLASS user DEFINITION.
PUBLIC SECTION.
DATA: name TYPE c LENGTH 30,
age TYPE i,
gender TYPE c LENGTH 1 READ-ONLY,
status TYPE c LENGTH 1.
CLASS-DATA : count TYPE i.
METHODS setname
IMPORTING namein TYPE c.
METHODS getname
EXPORTING nameout TYPE c.
METHODS setstatus
CHANGING newstatus TYPE c.
METHODS getstatustext
IMPORTING VALUE(statcode) TYPE c
RETURNING VALUE(stattext) TYPE string.
PRIVATE SECTION.
DATA : loginid TYPE c LENGTH 20,
pwd TYPE c LENGTH 15.
ENDCLASS.
CLASS user IMPLEMENTATION.
METHOD setname.
name = namein.
ENDMETHOD.
METHOD getname.
nameout = name.
ENDMETHOD.
METHOD setstatus.
IF newstatus CO 'MF'.
status = newstatus.
newstatus = '1'.
ELSE.
newstatus = '2'.
ENDIF.
ENDMETHOD.
METHOD getstatustext.
CASE statcode.
WHEN '1'.
stattext = 'Male'.
WHEN '2'.
stattext = 'Female'.
WHEN OTHERS.
stattext = 'Unknown'.
ENDCASE.
ENDMETHOD.
ENDCLASS.
CLASS car DEFINITION.
PUBLIC SECTION.
CLASS-DATA carnum TYPE i.
METHODS setnumseats
IMPORTING numseat TYPE i.
METHODS gofaster
IMPORTING increment TYPE i
EXPORTING result TYPE i.
METHODS goslower
IMPORTING decrement TYPE i
RETURNING VALUE(result) TYPE i.
PRIVATE SECTION.
DATA: make TYPE c LENGTH 20,
model TYPE c LENGTH 20,
seatsnum TYPE i,
speed TYPE i,
maxsped TYPE i.
ENDCLASS.
CLASS car IMPLEMENTATION.
METHOD setnumseats.
seatsnum = numseat.
ENDMETHOD.
METHOD gofaster.
DATA temspeed type i.
temspeed = speed + increment.
IF temspeed <= maxsped.
result = temspeed.
ELSE.
result = maxspeed.
ENDIF.
ENDMETHOD.
METHOD goslower.
DATA temspeed type i.
temspeed = speed - decrement.
IF speed >= 0.
result = temspeed.
ELSE.
result = 0.
ENDIF.
ENDMETHOD.
ENDCLASS.
REPORT ztest.
CLASS car DEFINITION.
PUBLIC SECTION.
CLASS-DATA carnum TYPE i.
METHODS constructor
IMPORTING
make TYPE c
model TYPE c
seatsnum TYPE i
speed TYPE i
maxsped TYPE i.
METHODS viewcar.
METHODS setnumseats
IMPORTING numseat TYPE i.
METHODS gofaster
IMPORTING increment TYPE i
EXPORTING result TYPE i.
METHODS goslower
IMPORTING decrement TYPE i
RETURNING VALUE(result) TYPE i.
PRIVATE SECTION.
DATA: make TYPE c LENGTH 20,
model TYPE c LENGTH 20,
seatsnum TYPE i,
speed TYPE i,
maxsped TYPE i.
ENDCLASS.
CLASS car IMPLEMENTATION.
METHOD constructor.
me->make = make. " make是constructor的参数, me->make是对象的属性
me->model = model.
me->seatsnum = seatsnum.
me->speed = speed.
me->maxsped = maxsped.
carnum = carnum + 1.
ENDMETHOD.
METHOD viewcar.
WRITE:/ make.
WRITE:/ model.
ENDMETHOD.
METHOD setnumseats.
seatsnum = numseat.
ENDMETHOD.
METHOD gofaster.
IF speed >= maxsped.
result = speed.
ELSE.
result = speed + increment.
ENDIF.
ENDMETHOD.
METHOD goslower.
IF speed >= 0.
result = speed - decrement.
ELSE.
result = 0.
ENDIF.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA car1 TYPE REF TO car.
CREATE OBJECT car1
EXPORTING
make = 'AUDI'
model = 'A4'
seatsnum = 5
speed = 60
maxsped = 120.
uline.
report Zclass.
class Zclass DEFINITION.
PUBLIC SECTION.
DATA:
mv_value TYPE String.
CLASS-DATA:
gv_value TYPE String.
METHODS:
set_value IMPORTING iv_value TYPE String.
display_value.
endclass.
Class zclass IMPLEMENTATION.
Method display_value.
Write:/ 'instance value:' mv_value.
Write:/ 'static value:' gv_value.
ENDMethod.
Mehtod set_value.
mv_value = iv_value.
ENDMethod.
ENDClass.
Start-of-selection.
DATA: lo_class TYPE REF TO zclass.
Create object lo_class.
Call Method lo_class->set_value
Exporting
iv_value = ' Hello A'.
zclass=>gv_value = 'Hello B'.
Call Method lo_class->display_value.
constructor这方法,用于在object实例化之后,来初始化一些属性的。它只能在public区,只能有importing的参数。
那么这里有一点要理解的就是,我类里面是确实设定了很多属性的。当我去定义方法的时候,方法里面要去import导入一些参数,虽然这些参数就是得和属性一样的类型,但是不要用一样的名字。类中创建了实例化对象之后,对象就把类的所有属性和方法拿过来了。当我去调用方法的时候,如果我首先执行了constructor方法来初始化一些类的属性,也就是在constructor方法定义的时候,我import的都是类的属性一样的对象。如果我用了一样的名字,那么系统就不知道谁是类的属性,谁是方法的参数。就像下面这样。
METHODS constructor
IMPORTING
make TYPE c
model TYPE c
seatsnum TYPE i
speed TYPE i
maxsped TYPE i.
class car IMPLEMENTATION.
method constructor.
make = make.
model = model.
seats = seats.
maxspeed = maxspeed.
numbers = numbers + 1.
ENDMETHOD.
而这个时候,就可以用到self-reference.
这个constructor是对于类的对象的构造方法。那么我要把import进来的参数赋值给对象。
所以加个me->就是告诉系统,左边的才是要赋值的对象的属性。
因为,当你执行这个constructor的方法的时候,它的import进去的参数,就会赋值给对象属性。
METHOD constructor.
me->make = make. " make是constructor的参数, me->make是对象的属性
me->model = model.
me->seatsnum = seatsnum.
me->speed = speed.
me->maxsped = maxsped.
carnum = carnum + 1.
ENDMETHOD.
8.4.1.4 全局类
那就是SE24来搞了。
在这里你看到有特性,接口,友元,属性,方法,事件,类型。
在特性页,类对象实例化生成。
public: 类可以在任意可见的地方进行初始化。
private:只能在类自身中创建类的实例。
protected:只能在类自身或者类的子类中创建类实例。
abstract:不可以创建实例。
一般选public。
属性:
就是类的数据了。数据的具体值就是以后对象的值了。
那么一个个的第一个level:
就是属性的三个定义级别,分别是实例变量属性,静态属性和常量。
instance attribute既然是实例变量属性,就是说它这个类里的要经过创建成为对象实例之后才能使用它。对类的实例化的对象有效,每一个对象的实例化属性都有自己的唯一值。相当于局部类中用DATA声明的变量。
static attribute静态变量,相当于全局共享。类的所有对象都能用。但是任何一个对象对它的修改都将影响全局。
一般还是别用对象实例方法来改了,搞一个类的静态方法来访问修改。
可以通过对象访问,或者类名访问:
DATA: lo_class TYPE REF TO zcl_class.
CREATE OBJECT lo_class.
lo_calss -> static_name = '001'.
zcl_class => static_name = '002'.
跟这个手动声明的CLASS-DATA一样的。注意就是它只能在PUBLIC里面。
CLASS zcl_class DEFINITION.
PUBLIC SECTION.
CLASS-DATA:
static_name TYPE String.
ENDCLASS.
常量属性。要附初始值。就是自己手动写的时候那个CONSTANTS.
除此之外,还有个可视:
public 就是数据在任何类和程序都可以访问。属性一般弄成public,方法一般弄成public.
protected 在类本身,类子类,类友元中可见。一般属性都是私有或者保护的。类有个共有的set和get方法对属性进行存取,保证数据格式和合法性验证。
private 类本身和类友元可见。
Read Only的话,是对外read only。类自己的方法还是可以改它的属性的。就算是public了,也不能被外面修改。
属性的定义方式。
LIKE跟着已经实例化的数据变量或者对象。
TYPE后面是数据类型,或者SE11里面的表的字段啥的。
TYPE REF TO类。
TYPE BOXED 嵌套结构。
参照已有或者自己代码types定义。
或者在TYPES里面也能看到代码里定义的type。
方法 就是说类的功能。有了数据再有了功能才能实现。
类的方法就是类对象的状态改变和消息传递的行为方式。代表类对象可以被外部执行的活动。也就是被外部程序调用。
类方法的设计一般功能最小化单一原则。一个方法只处理一件事。代码行数不超过一屏。
方法包括实例方法和静态方法。实例方法必须通过类的实例来使用。
静态方法就相当于全局方法,不属于类的对象而属于全局类。静态方法主要用来对应操作它的静态属性。用类=> 或者对象->来调用。
静态方法里只能调用类的其他静态方法,访问类的静态属性数据。public和protected的静态方法可以被子类继承,但静态方法不可以在子类中被重新定义。
类的构造器是静态方法,对象构造器是实例方法。
CLASS_CONSTRUCTOR Static Method
CONSTRUCTOR Instance Method
插入
传引用和传值的区别
在方法参数中,不勾就是传引用。引用就是变量的存放地址。方法内的参数变量和方法外的实际变量共享地址内的值。方法内进行值改变,方法外的值也改变。
如果是returning那就是传一个值了,默认是pass value钩上的。
如果是传值,意思是将数据的值复制一份到另一个地址里,然后把值传入方法中。
插入结束
事件 就是说通过程序触发一个事件,然后来执行一个方法。
事件没代码,可以有参数。
建事件之后,建方法。建了方法进去把它设置在事件后运行。把Event Handler for 打钩,然后填写类和event。
也就是说当这个event被触发,那么这个方法就会自动执行。
Method Type里面就是用来标识这些的:
那么event就是用RAISE EVENT去写了。
8.4.4 接口
接口是啥呢?用来干啥的呢?
当两个相似的类有个同名的方法,但是不同的功能。就上接口了。
接口有点像类,但是接口中的方法是在类中实现的,是对类的扩展。实现接口后,接口将成为类的公有成员,接口加上继承,是多态的基础。就是说啥呢?
在一个接口中定义的方法在不同类中有不同的实现,接口没有自己的实例, 因而也不需要进行方法实现。
INTERFACE <intf_name>.
DATA.....
CLASS-DATA.....
METHODS.....
CLASS-METHODS.....
ENDINTERFACE.