在程序中, 对象的识别和寻址是通过对象引用来实现的,对象引用变量可以访问对象的属性和方法.
ABAP 中的类也有程序内部的局域类和全局类之分, 其作用范围不同.
ABAP中的全局类是通过ABAP工作台中的类创建工具 Class Builder SE24开发的, 保存在系统类库程序 Class Pool 中, 可供所有程序作用.
一. 程序中类的定义.
1. 类声明及实现
* Class Declarations
CLASS application DEFINITION.
PUBLIC SECTION.
METHODS: show_text.
PRIVATE SECTION.
DATA text(100) TYPE c VALUE 'This is my first ABAP Object.'.
ENDCLASS.
* Class Implementation
CLASS application IMPLEMENTATION.
METHOD show_text.
WRITE text.
ENDMETHOD.
ENDCLASS.
2. 成员可见性.
①公有: 可被所有对象使用.
PUBLIC SECTION.
②保护: 只能被本类及其派生类中的方法使用.
PROTECTED SECTION.
③私有: 只能被类本身的方法所使用.
PRIVATE SECTION.
公有部分常常用来定义类的方法, 而类属性则一般定义为类的私有或保护数据, 只能通过自身或其派生类的方法来操作.
3. 成员类型.
可定义三种不同类型的成员, 分别是属性, 方法和事件.
属性是在类内部的数据对象, 对于实例属性, 用 DATA 定义, 语法与数据对象声明相同;
对于静态属性(类属性), 使用CLASS-DATA 语句定义, 在类的声明部分定义.
* Class Declarations
CLASS application DEFINITION.
PUBLIC SECTION.
CLASS-DATA class_name(10) type c value 'Vehicle'. "静态属性
PRIVATE SECTION.
DATA speed TYPE i.
ENDCLASS.
对于公有组件还可以在属性定义语句中增加 READ-ONLY 关键字, 则该属性不能被类外部读取但可被类的内部方法所修改.
除 DATA 外, 也可以使用 CONSTANT 语句定义类常量, 必须在类定义时指定其值.
使用 TYPES 语句在类中定义的数据类型, 相当于静态属性, 其定义方法与普通 ABAP 程序类似.
类方法需要在类的声明和实现两部分进行定义, 在类的声明部分说明方法的参数接口, 在实现部分则通过 ABAP代码完成具体的功能.
方法的声明关键字为 METHODS 和 CLASS-METHODS.
METHODS meth
IMPORTING ... i1 TYPE ...
EXPORTING ... e1 TYPE ...
CHANGING ... c1 TYPE ...
EXCEPTIONS ... x1 TYPE ...
定义了方法及方法的参数接口, 和 FORM 相似, 但没有 TABLES 参数.
CLASS vehicle DEFINITION.
PUBLIC SECTION.
CLASS-DATA class_name(10) TYPE c VALUE 'Vehicle'.
METHODS: accelerate,
show_speed.
PRIVATESECTION.
DATA speed TYPE i.
ENDCLASS.
CLASS vehicle IMPLEMENTATION.
METHOD accelerate.
speed = speed + 1.
ENDMETHOD.
METHOD show_speed.
WRITE: / 'Speed: ', speed.
ENDMETHOD.
ENDCLASS.
二. 定义系统全局类.
通过 Class Builder 工具在类型为 K 的 ABAP 类库程序中定义.
如果在一个程序中使用该类,则整个类库将被载入到程序内存中.
1. 全局类定义.
可以用 SE80 或 SE24 进入 Class Builder 进行创建.
实例生成:
1、public:决定该类的实例可以被所有的用户创建。
2、protected:只能被其本身或者派生类创建。
3、private:只能通过其自身的方法创建。
选了“最终”则说明全局类不能在程序中被继承。
使用全局类.
DATA: OB_VE TYPE REF TO Z_CL_VEHICLE.
START-OF-SELECTION.
CREATE OBJECT OB_VE.
CALL METHOD OB_VE->ACCELERATE
.
CALL METHOD OB_VE->SHOW_SPEED.
三. 程序中的类实例.
使用对象的步骤如下:
1) 定义类的声明和实现方法;
2) 使用 DATA 语句中的 TYPE REF TO 参照类类型声明引用变量;
3) 使用CREATE OBJECT 语句创建对象;
4) 通过 ->或 => 运算符访问对象或类组件;
1. 对象创建与引用;
ABAP 对象的创建和访问需要通过对象引用进行, 引用类型是ABAP基本类型之一,
其中包括数据引用和对象引用,
其中对象引用又包括类引用和接口引用.
对于普通类来说, 应使用类引用.
在程序中, 需要先根据对象类型声明引用类型变量, 然后对该变量引用ABAP对象, 该引用变量中实际上存储的是ABAP对象的内在地址,
因而该引用类型变量也就是普通意义上的指向对象的指针.一个引用类型变量可以不指向任何内存地址或指向一个数据对象,
但一个ABAP对象则可以同时存在多个指向它的指针, 可以通过所有这些指针对其进行操作.
DATA ob_vehicle TYPE REF TO vehicle.
CREATEOBJECT ob_vehicle.
其中DATA语句创建了一个引用数据对象ob_vehicle , 该数据对象的类型定义为"指向一个类为vehicle的对象的指针".
定义指针之后,CREATE OBJECT 语句则创建了一个类vehicle的实例, 并同时将该对象的地址指针赋给引用类型ob_vehicle.实际情况如下图所示:
引用类型与其它类型的数据类型相同, 同类型变量之间也可以相互赋值, 成功赋值之后二者将指向相同的对象.
2. 访问对象组件.
对象创建之后, 可以通过指向它的指针(引用变量)对其进行操作. 可以使用的对象组件一般为当前可见的属性和方法,
通过引用变量后接运算符 -> 访问对象组件. -> 即可以访问类中定义的实例组件又可以访问静态组件.
但对于静态组件还有另一种访问方式, 通过类名称本身=>直接访问.
oref->comp
其中oref代表引用变量, comp 代表类成员.
class=>comp
其中class代表类名.
由于引用类型变量本身也可以被定义为类属性, 因而可以出现下述的链式访问结构:
oref1->oref2->comp
或者
class=>oref->comp
上述的两种运算符是类的外部访问, 在某些情况下, 对象本身需要知道本身的地址, 可以使用局部引用变量 ME,
该变量隐式的存在于所有的实例方法中, 当方法被调用时, 该变量将包含自身的地址指针, 这种用法称为对象的自身引用.
3. 删除对象
对象占据程序内存, 如果有后续程序中不再使用的对象, 应该将其删除,
系统对象的删除操作与引用变量有关, 系统将自动定时进行检测, 如果一个对象不再被任何变量引用, 则将其删除, 并释放其所占的内存,
这个操作是通过垃圾回收机制来完成的.
垃圾回收机制定时的清除不再使用的对象, 使用 CLEAR 语句初始化所有指向该对象的引用变量或者将引用变量的值改变, 都将触发回收机制.
四. 方法.
1. 参数的定义和传递.
类方法的参数接口部分在在类声明部分通过METHODS语句定义的, 而且该接口不需要在类的实现部分再次出现, 参数接口的详细语法形式为:
METHODSmeth
IMPORTING ... [VALUE(i1)|i1] TYPE type [OPTIONAL|DEFAULT def1] ...
EXPORTING ... [VALUE(e1)|e1] TYPE type [OPTIONAL] ...
CHANGE ... [VALUE(c1)|c1] TYPEtype [OPTIONAL|DEFAULT def1] ...
RETURNING VALUE(r)
EXCEPTIONS ... X1 ...
2. 调用方法时, 除去指定为可选的参数之外, 所有的参数都必须传递相应的实参值.
CALLMETHOD [oref->|class=>]meth
EXPORTING... i1 = f1 ...
IMPORTING ... e1 = g1 ...
CHANGING ... c1 = c1 ...
RECEIVING r = h
EXCEPTIONS ... x1 = rc1 ...
该语句格式适用于包括类内部直接调用, 实例调用(oref->)和静态调用(class=>)三种情况. 其中参数定义时用IMPORTING选项指定的形参 i 此时将接受 CALL METHOD 语句中用EXPORTING 中传递的实参 f; 而用EXPORTING 选项指定的形参 e 此时将通过 IMPORTING 选项传递给实参 g.
如果方法中只包含输入参数, 而没有输出参数, 则可以使用下列几种形式的简略形式对参数进行调用.
CALL METHOD [oref->|class=>]method().
CALL METHOD [oref->|class=>]method(f).
CALL METHOD [oref->|class=>|method(...i1 = f1...).
Example:
CLASS VEHICLE DEFINITION.
PUBLIC SECTION.
CLASS-DATA CLASS_NAME(10) TYPE C VALUE 'Vehicle'.
METHODS:SHOW_SPEED,
ACCELERATE IMPORTING RATE TYPE I.
PRIVATE SECTION.
DATA SPEED TYPE I.
ENDCLASS.
CLASS VEHICLE IMPLEMENTATION.
METHOD ACCELERATE.
SPEED = SPEED + RATE.
ENDMETHOD.
METHOD SHOW_SPEED.
WRITE: / 'Speed: ', SPEED.
ENDMETHOD.
ENDCLASS.
DATA: OB_VEH TYPE REF TO VEHICLE.
START-OF-SELECTION.
CREATE OBJECT OB_VEH.
CALL METHOD OB_VEH->ACCELERATE EXPORTING RATE = 2.
CALL METHOD OB_VEH->ACCELERATE EXPORTING RATE = 2.
CALL METHOD OB_VEH->SHOW_SPEED.
方法还可以进行动态调用, 即通过变量对方法名进行指定, 例如下列语句:
meth = accelerate.
CALL METHOD ob_veh->(meth).
实参的动态传递:
CALL METHOD [oref->|class=>]meth
PARAMETER-TABLE ptab
EXCEPTION-TABLE etab.
其中实参必须在程序中放置在两个特定类型的内表中, 传递给方法, 其中参数表ptab可以参照类型组ABAP_PARMBID(行类型)和ABAP_PARMBIND_TAB(表类型)生成;
异常表etab 可以参照ABAP_EXCBIND(行类型)和ABAP_EXCBIND_TAB(表类型)生成.
3. 函数方法.
函数也是一种过程模块, 其特殊之处是在调用时可以使用操作符(=)来替代过程调用语句, 并可作为操作数直接应用于操作语句中, 因为函数只返回一个唯一的值.
在ABAP Objects 语法中, 可以用例如函数方法来实现这种函数功能, 形式如下:
METHODSmeth
IMPORTING... [ VALUE(i1)|i1] TYPE type [OPTIONAL|DEFAULT def1] ...
RETURNING VALUE(r).
一个函数方法可以有多个数量的输入参数, 但只能有一个输出参数, 且该输出参数必须为值传递而且需要完成限定其类型.
函数方法的调用格式如下:
CALL METHOD [oref->|class=>]meth
EXPORTING... i1 = f1 ...
RECEIVING r = h.
简化的调用形式如下:
... [oref->|class=>]method( ) ...
... [oref->|class=>]method(f) ...
... [oref->|class=>]method(... i1 = f1 ...)...
CLASS CIRCLE DEFINITION.
PUBLIC SECTION.
METHODS GET_AREA IMPORTING VALUE(I_RADIUS) TYPE I
RETURNING VALUE(R_SIZE) TYPE F.
PRIVATE SECTION.
CONSTANTS PI TYPE F VALUE '3.1415926'.
ENDCLASS.
CLASS CIRCLE IMPLEMENTATION.
METHOD GET_AREA.
R_SIZE = I_RADIUS ** 2 * PI.
ENDMETHOD.
ENDCLASS.
DATA: OB_CIRCLE TYPE REF TO CIRCLE,
AREA TYPE F.
PARAMETERS: RADIUS TYPE I.
START-OF-SELECTION.
CALL METHOD OB_CIRCLE->GET_AREA
EXPORTING
I_RADIUS = RADIUS
RECEIVING
R_SIZE = AREA.
WRITE: / AREA.
AREA = OB_CIRCLE->GET_AREA( RADIUS ).
WRITE: / AREA.
4. 构造和析构方法.
1). 构造方法是运行时环境自动调用的一种方法, 用于为对象设定一个初始化状态, 不能在程序运行过程中由程序代码调用.
实例构造方法在每一次对象创建的过程中(CREATE OBJECT语句时)被系统调用, 如果在类声明部分进行声明, 则需要在实现部分进行代码实现.
METHODS constructor
IMPORTING... [VALUE(i1)|i1] TYPE type [OPTIONAL|DEFAULT def1] ...
EXCEPTIONS... e1 ...
构造方法中不包含任何形式的输出参数, 其功能只是定义对象状态, 而不是对其进行改变.
类构造方法属于静态方法, 只在程序中被调用一次, 即第一次使用该类时调用,
该方法也必须在公有部分声明, 其名称必须为 CLASS_CONSTRUCTOR.
CLASS-METHODS class_constructor.
类构造方法不包含接口, 而且在其中只能访问类的静态属性.
CLASS VEHICLE DEFINITION.
PUBLIC SECTION.
CLASS-DATA CLASS_NAME(10) TYPE C VALUE 'Vehicle'.
METHODS:SHOW_SPEED,
ACCELERATE IMPORTING RATE TYPE I,
CONSTRUCTOR IMPORTING I_SPEED TYPE I. "构造函数
PRIVATE SECTION.
DATA SPEED TYPE I VALUE 0.
ENDCLASS.
CLASS VEHICLE IMPLEMENTATION.
METHOD CONSTRUCTOR.
SPEED = I_SPEED.
ENDMETHOD.
METHOD ACCELERATE.
SPEED = SPEED + RATE.
ENDMETHOD.
METHOD SHOW_SPEED.
WRITE: / 'Speed: ', SPEED.
ENDMETHOD.
ENDCLASS.
DATA: OB_VEH TYPE REF TO VEHICLE.
START-OF-SELECTION.
CREATE OBJECT OB_VEH EXPORTING I_SPEED = 4. "构造函数
CALL METHOD OB_VEH->SHOW_SPEED.
CALL METHOD OB_VEH->ACCELERATE
EXPORTING
RATE = 2.
CALL METHOD OB_VEH->ACCELERATE
EXPORTING
RATE = 2.
CALL METHOD OB_VEH->SHOW_SPEED.
五. 继承
ABAP 所有的类都是默认继承了系统中的空类OBJECT.
具有一般性的类称为基类(Superclass),其各个子类称为派生类(Subclass).
在类定义时, 使用 INHERTING FROM 附加项可以指定派生类和基类之间的继承关系.
CLASS c2 DEFINITION INHERITING FROM c1.
...
ENDCLASS.
ABAP 中的继承为单一继承, 即一个类可以有多个派生类, 但只能有一个直接基类.
在继承过程中各成员的组件可见性如下:
一个派生类中的公有成员包括其本身公有部分定义的成员以及所有基类公有成员, 这些公有成员可以通过选择运算符"->" 在类外部获得.
一个派生类中的被保护成员包括其本身被保护部分定义的成员以及所有基类的被保护成员.
这些成员不能通过组件选择运算符"->" 在类外部获得, 但可以在派生类内部使用. 在类外部看, 其行为与类私有成员完全一致.
一个派生类中的私有成员只包括其本身私有部分定义的成员. 这些成员只能在派生类内部使用.
因而, 继承类和基类的公有成员和被保护成员享有共同的命名空间, 而私有成员则在不同类之间可以出现重名情况.
参照基类声明的引用变量可以指向其继承类的对象, 如上图所示,
对象 n 是类 Class3的一个实例, 而左侧的三个引用变量CREF1, CREF2, CREF3 则分别参数类Class1, Class2, Class3 生成.
这三个类存在继承关系. 参考基类声明的引用变量也可以指向其派生类, 即三个变量均可以指向对象 n,
但要注意的是 CREF1 只能访问 Class1 的公有组件, 而 CREF2可以访问 Class1 和 Class2的公有组件, CREF3 则可以访问全部类的公有组件.
但特别要注意的是: 尽管基类引用变量可以指向其继承类对象, 然而反之则被视为错误的类型转换.
多态性的继承实现.
在继承中, 多态性是通过方法重载及引用变量实现的, 即子类可以重新定义并以不同的方式实现基类中的公有或保护方法, 此时基类中的方法需为实例方法.
在派生类中要这样声明:
METHOD meth REDEFINITION.
该方法必须和基类中的方法具有相同的接口, 但可以通过不同的代码实现.
在方法重载过程中, 如果在派生类内部仍然需要使用基类中的方法, 则可以使用 SUPER 关键字来指定其基类.
CALL METHOD super->meth.
Example:
*&---------------------------------------------------------------------*
*& Report Z_AF_050
*&
*&---------------------------------------------------------------------*
*&
*&
*&---------------------------------------------------------------------*
REPORT z_af_050.
CLASS class1 DEFINITION INHERITING FROMobject. "基类
PUBLICSECTION.
METHODS:accelerate,
write_status.
PROTECTEDSECTION.
DATA:speed TYPE i.
ENDCLASS.
CLASS plane DEFINITION INHERITING FROM class1.
PUBLICSECTION.
METHODS:rise,
write_status REDEFINITION. "多态
PROTECTEDSECTION.
DATAaltitude TYPE i.
ENDCLASS.
CLASS ship DEFINITION INHERITING FROM class1.
PUBLICSECTION.
METHODS:write_status REDEFINITION. "多态
ENDCLASS.
CLASS class1 IMPLEMENTATION.
METHODaccelerate.
speed =speed + 1.
ENDMETHOD.
METHODwrite_status.
WRITE: /'Speed:', speed.
ENDMETHOD.
ENDCLASS.
CLASS plane IMPLEMENTATION.
METHODrise.
altitude= altitude + 2.
ENDMETHOD.
METHODwrite_status.
WRITE: 'Plane:'.
CALLMETHOD super->write_status.
WRITE: /'Altitude:', altitude.
ENDMETHOD.
ENDCLASS.
CLASS ship IMPLEMENTATION.
METHODwrite_status.
WRITE: /'Ship speed:', speed.
ENDMETHOD.
ENDCLASS.
DATA: plane_ref TYPE REF TO plane.
DATA: ship_ref TYPE REF TO ship.
START-OF-SELECTION.
CREATEOBJECT: plane_ref,
ship_ref.
CALLMETHOD: plane_ref->accelerate,
plane_ref->rise,
plane_ref->write_status,
ship_ref->accelerate,
ship_ref->write_status.
抽象类和抽象方法.
一个基类可能包含多个派生类, 但该基类只是作为模板出现的, 并不需要有任何对象作为实例, 则可以将该类声明为抽象类(Abstract Class).
CLASS class1 DEFINITION ABSTRACT.
...
ENDCLASS.
抽象类不能使用CREATE OBJECT 语句创建类对象, 其主要目的是为了做派生类的模板, 实际使用的对象则依照各个派生类创建.
也可以将方法定义为抽象方法.
METHODS meth ABSTRACT ...
一个抽象方法不能在类本身中实现, 而必须在其派生出的非抽象类中实现. 因而含有抽象方法的类必须定义为抽象类.
最终类和最终方法.
最终类(FinalClass), 是不能被继承的类, 其主要意义在于: 设计过程中, 如果一个类有很多级别的派生类, 则其中公有和被保护成员方法, 随意改动该类, 很容易造成派生类中的语法和主义冲突. 因而, 如果不希望在程序中继续对该类进行派生, 则可以定义该类为最终类.
CLASS class1 DEFINITION FINAL.
...
ENDCLASS.
最终方法是不可以重新定义的方法, 不一定出现在最终类中, 但最终类中的所有方法都是最终方法.
METHODS meth ABSTRACT ...
六. 接口.
1. 接口的定义与实现.
Interfaces 即可以保证这些类外部看起来具胡一致性,标准化的接口, 又可以在不同的类内部使用不同的实现方法, 而这个具体实现过程是类外部的用户无需关心的.
接口是一个独立结构, 可以在其中定义一些成员并在具体类中实现, 其作用是对类中已经定义的成员进行扩展. 实现接口后,接口将成为类的公有成员, 但类可以自行对接口中的方法以其自身特定形式实现. 这样, 对于用户来说, 不同的类对象包含相同的操作接口(即接口中定义的成员名称), 但程序内部的具体实现则根据类的不同而有所区别. 接口是OOP中除继承之外的另一个主要多态性的实现机制技术.
在程序中定义接口.
INTERFACE intf.
DATA ...
CLASS-DATA...
METHOD ...
...
ENDINTERFACE.
在接口定义内部可以声明的成员与类中的成员相同, 但无需注明具体的可见性, 因为具体类中实现的所有接口组件均为公有成员. 同时, 接口定义中也只包含成员声明部分, 而具体的实现也将在具体类中进行.
实现接口.
接口没有自己的实例, 因而也不需要进行方法实现, 其中方法的实现要通过具体的类进行.
语法格式如下:
CLASS class1 DEFINITION.
PUBLICSECTION.
...
INTERFACES: int1, int2.
...
ENDCLASS.
在类定义中, 接口的实现只能出现在公有部分, 接口中定义的所有组件都将被添加为该类的公有成员.
接口中的成员通过接口名称 +" ~ " + 成员方式访问.intf~comp.
在类的实现部分, 必须包含所有的接口方法实现.
CLASS class IMPLEMENTATION.
...
METHODintf1_imeth1.
...
ENDMETHOD.
...
METHODintf2_imeth2.
...
ENDMETHOD.
...
ENDCLASS.
一个接口可以被任意多个不同的类实现, 该接口中定义的成员集在各个类中名称相同(形成了一个标准接口), 然而各个类中成员的方法的实现方式则可以有差异, 也就形成了多态性. 如果一个类中除接口之外没有定义任何类自身的公有成员方法, 则接口就成了该类中的全部"外部接口".
*&---------------------------------------------------------------------*
*& Report Z_AF_051
*&
*&---------------------------------------------------------------------*
REPORT z_af_051.
INTERFACE status. " Define a interface
METHODSwrite.
ENDINTERFACE.
CLASS counter DEFINITION.
PUBLICSECTION.
INTERFACES status.
METHODSincrement.
PRIVATESECTION.
DATAcount TYPE i.
ENDCLASS.
CLASS counter IMPLEMENTATION.
METHOD status~write. " 在接口的实现部分以 stauts~write 实现.
WRITE: /'Count in counter is ', count.
ENDMETHOD.
METHODincrement.
ADD 1 TOcount.
ENDMETHOD.
ENDCLASS.
CLASS bicycle DEFINITION.
PUBLICSECTION.
INTERFACES status.
METHODSdrive.
PRIVATESECTION.
DATAspeed TYPE i.
ENDCLASS.
CLASS bicycle IMPLEMENTATION.
METHODstatus~write.
WRITE: /'Speed of bicycle is', speed.
ENDMETHOD.
METHODdrive.
ADD 10 TOspeed.
ENDMETHOD.
ENDCLASS.
DATA: count TYPE REF TO counter,
bikeTYPE REF TO bicycle,
statusTYPE REF TO status,
status_tab TYPE TABLE OF REF TO status.
START-OF-SELECTION.
CREATEOBJECT: count, bike.
DO 5 TIMES.
CALLMETHOD: count->increment,
bike->drive.
ENDDO.
APPEND:count TO status_tab,
bike TO status_tab.
LOOP ATstatus_tab INTO status.
CALLMETHOD status->write.
ENDLOOP.
该例中包含两个完全无关的类 bike 和 count, 但实现了相同的接口 status, 因而这两个类都包含同样的方法 write, 该方法是在接口中定义的.
接口引用.
引用变量是访问 ABAP 对象的途径, 与基于类声明的引用变量类似, 也可以基于接口声明引用变量, 即接口引用. 接口引用也是一种对象引用, 对于包含接口的类对象, 除通过类引用访问之外, 还可通过接口引用进行访问. 接口引用可以指向任何实现了该接口的对象.
YPTES|DATA iref TYPE REF TO intf.
其中 intf 是全局或程序中已经定义的接口.
通过接口引用访问对象.
要访问一个类对象, 必须先声明一个基于该类的引用变量, 如果该类为一个接口的实现, 则可以将该车胎变量赋给一个接口变量, 此时接口引用与类引用指向相同的类对象.
假设接口变量名称为 inef,类引用名称为 cref, 赋值语法格式如下:
iref = cref.
使用类引用访问:
cref->intf~attr.
CALL METHOD cref->intf~meth.
使用接口引用访问属性和方法的语法格式如下:
iref->attr.
CALL METHOD iref->meth.
对于接口中定义的静态成员, 如果该成员是常量, 则只能通过接口引用进行访问:
intf=>const
对于其他静态接口成员, 则可以通过实现该接口的类本身或者类引用进行访问:
class=>intf~attr.
CALL METHOD class=>intf~meth.
在接口引用间赋值.
与类引用类似, 可以将接口引用分配给不同的引用变量, 还可以在类引用和接口引用之间相互赋值.
假设存在类引用 cref 和接口引用iref, iref1 和 iref2.
iref1 = iref2.
系统将进行静态语法检查, 这两个接口必须参照同一个接口声明, 或者 iref2 所参照的接口是接口 iref1 的一个成员.
如果使用形式:
iref = cref.
则 cref 参照的类必须是 ABAP 预定义的空类 OBJECT.
对于所有其他情况, 则必须通过强制赋值语句或者强制赋值运算符 "?="进行.
MOVE iref to cref.
cref ?= iref.
在进行强制赋值时, 系统不会出现任何静态语法检查, 但系统将在运行时检查目标对象引用变量是否可以指向源变量引用的对象. 如果允许则进行赋值, 否则将触发可以捕获的运行时错误MOVE_CASE_ERROR.
如果将接口引用赋给类引用, 且该类不是参照OBJECT声明,则必须使用强制赋值, 要使赋值过程成功, 接口引用 iref所指向的对象中必须与类引用cref参照的类相同.
七 事件.
事件是除去属性, 方法之外的第三类成员. 这三种组件中, 属性用于刻画类对象的状态, 方法用于访问或修改对象状态, 而事件则用于一个类对象发布其状态的改变, 因而其他对象可以捕获该方法并作出响应.
在ABAPObject 中, 事件的触发和处理是通过特定的方法进行的, 一个方法作为触发者触发事件, 而程序中的另一个方法则作为处理者捕获并处理该事件, 处理方法在事件出现进被执行.
声名事件.
事件可以在类或接口的声名部分进行声明.
EVENTS|CLASS-EVENTS evt
EXPORTING... VALUE(e1) TYPE type [OPTIONAL] ...
声明该事件时, 可以使用 EXPORTING 附加项指定需要向事件处理方法中传递的参数, 该参数传递方法恒为值传递. 实例事件总是包含一个隐含的参数 SENDER, 该参数的类型为触发事件的类或接口的对象引用.
触发事件.
一个实例事件可以被类中的任意方法触发, 静态事件则可以被静态方法触发.
RAISE EVENT evt EXPORTING ... e1 = f1 ...
对于每一个没有被指定OPTIONAL 项的形参 ei, 均必须在实例方法中传递一个实参 fi, 自身引用 ME 对象在被默认的传递到隐含参数 SENDER.
事件处理方法
事件需要通过方法捕获事件并进行处理, 必须首先为该事件定义一个事件处理方法, 然后在运行时为该事件进行注册.
声明事件处理方法
任何类都可以为其它类中定义的事件进行处理, 同时也可以为自身事件进行处理.
实例事件处理方法的语法格式如下:
METHODS|CLASS-METHODS
meth FOREVENT evt OF cif IMPORTING ... ei ...
一个事件处理方法的接口中只能包含定义在事件 evt 中的形式参数, 并需遵循事件中的参数属性设定, 但事件处理方法不需要使用所有 RAISE EVENT 中定义的参数.
注册事件处理方法.
要使事件处理方法能够对事件进行响应,必须在运行时为相关事件注册该方法, 语法格式如下:
SET HANDLER ... hi ... [FOR] ...
该语句将处理方法和相关的触发方法进行链接, 其语法和使用效果部分地取决于事件的类型.
定义在类中的实例事件.
定义在接口中的实例事件.
定义在类中的静态事件.
定义在接口中的静态事件.
对于实例事件, 在进行处理之前必须在注册语句中使用附加项 FOR 指定希望注册到的对象.
可以指定一个单独的对象作为事件触发者.
SET HANDLER ... hi ... FOR ref.
或者使用 FORALL INSTANCES 附加项为处理器注册所有可以触发该事件的实例;
SET HANDLER ... hi ... FOR ALL INSTANCES.
该注册过程可以作用于目前尚未被创建的实例.
对于静态事件, 则不能使用 FOR 附加项;
SET HANDLER ... hi ...
该注册自动应用于整个类, 对于接口中的静态事件, 该注册可以应用于目前尚未装载至内在中的类.
事件处理时间.
在程序执行到 RAISEEVENT 语句之后, 所有已注册的处理方法都将在下一个语句之前被处理. 如果处理方法本身触发事件, 则该处理方法在原方法继续执行之前被重新调用.
事件的触发及处理.
事件的触发和处理基本过程如下: 假设有两个类 C1 和 C2, C1中包含事件 E1, 而该事件可能在方法 M1 中通过 RAISE EVENT 语句被触发, 而 C2 中将包含方法 M2, 该方法可以捕获事件 E1, 并进行处理.
在实际操作时, 要使一个对象的事件处理方法能够捕获另一个对象触发事件, 必须先使用 SET HANDLER 语句进行注册, 这个注册过程中形成了触发对象和响应对象之间的对应关系.
当然, 也可以同时注册多个处理对象与一个触发对象相对应.
SET HANDLER 语句则创建一个系统内部的事件处理表, 为每一个事件注册相应的方法. 该表为系统隐含. 对于实例事件, 事件处理表中包含用于实现处理的方法名称以及注册了的类实例引用, 该表通过 SET HANDLER 语句动态的进行维护. 对于静态事件, 系统为相关类创建一个实例无关的处理表. 一旦事件被触发, 系统将查找相关的事件表并执行对应于该事件的实例方法.
*&---------------------------------------------------------------------*
*& Report Z_AF_052
*&
*&---------------------------------------------------------------------*
REPORT z_af_052.
CLASS class1 DEFINITION INHERITING FROM object.
PUBLICSECTION.
EVENTS:too_fast. " 声明事件,如果有参数恒为值传递
METHODS:accelerate,
show_speed.
PROTECTEDSECTION.
DATA:speed TYPE i.
ENDCLASS.
CLASS class1 IMPLEMENTATION.
METHODaccelerate.
speed =speed + 1.
IF speed> 5.
RAISEEVENT too_fast. "触发事件
speed =5.
ENDIF.
ENDMETHOD.
METHODshow_speed.
WRITE: /'Speed:', speed.
ENDMETHOD.
ENDCLASS.
*事件需要通过方法捕获事件并进行处理,必须首先为该事件定义
*一个事件处理方法,然后在运行时为该事件进行注册.
*声明事件处理方法,任何类都可以为其他类中定义的事件进行处理,
*当然也可以为自身事件进行处理.
CLASS handler DEFINITION.
PUBLICSECTION.
METHODShandle_excess FOR EVENT too_fast OF class1.
ENDCLASS.
CLASS handler IMPLEMENTATION.
METHOD handle_excess.
WRITE: /'Speed can not be too fast.'.
ENDMETHOD.
ENDCLASS.
DATA: o_class1 TYPE REF TO class1,
o_handle TYPE REF TO handler.
START-OF-SELECTION.
CREATEOBJECT: o_class1,
o_handle.
*要使事件处理方法能够对事件进行响应, 必须在运行时为相关事件注册该方法.
SET HANDLERo_handle->handle_excess FOR ALL INSTANCES.
DO 11TIMES.
CALLMETHOD o_class1->accelerate.
CALLMETHOD o_class1->show_speed.
ENDDO.