1 继承
1 多态性的继承实现
在继承中,多态性是通过方法重载及引用变量实现的。即子类可以重新定义 并以不同的方式实现基类中的公有或保护方法,此时类中的方法需为实例方法。在派生类的声明方法,需如下声明MEHTOD meth REDEFINITION.
该方法必须和基类中的方法具有相同的接口,但可以通过不同的代码实现。如果有一个相同的基类,包含多个派生类,则这些派生类都可以重新定义实例方法,从类用户角度讲,不同的类中都存在该操作,但具体实现过程各有不同。当访问基类时,将采用基类中的原始方法实现。访问派生类时,则采用新的派生类方法实现,原有的基类方法被屏蔽,包括通过自身引用运算符“me->”调用方法。
在方法重载过程中,如果在派生类内部仍然需要使用基类中的方法,则可以使用SUPER为关键字来指定其基类CALL METHOD super->meth.
REPORT z_method_redefinition.
CLASS vehicle DEFINITION.
PUBLIC SECTION.
METHODS: accelerate, write_status.
PROTECTED SECTION.
DATA speed TYPE i.
ENDCLASS.
CLASS plane DEFINITION INHERITING FROM vehicle.
PUBLIC SECTION.
METHODS: rise, write_status REDEFINITION.
PROTECTED SECTION.
DATA altitude TYPE i.
ENDCLASS.
CLASS ship DEFINITION INHERITING FROM vehicle.
PUBLIC SECTION.
METHODS write_status REDEFINITION.
ENDCLASS.
CLASS vehicle IMPLEMENTATION.
METHOD accelerate.
speed = speed + 1.
ENDMETHOD.
METHOD write_status.
WRITE: / 'Speed:', speed.
ENDMETHOD.
ENDCLASS.
CLASS plane IMPLEMENTATION.
METHOD rise.
altitude = altitude + 1.
ENDMETHOD.
METHOD write_status.
WRITE: / 'Plane:'.
CALL METHOD super->write_status.
WRITE: / 'Altitude:', altitude.
ENDMETHOD.
ENDCLASS.
CLASS ship IMPLEMENTATION.
METHOD write_status.
WRITE: / 'Ship speed:', speed.
ENDMETHOD.
ENDCLASS.
DATA: plane_ref TYPE REF TO plane,
ship_ref TYPE REF TO ship.
START-OF-SELECTION.
CREATE OBJECT: plane_ref, ship_ref.
CALL METHOD: plane_ref->accelerate,
plane_ref->rise,
plane_ref->write_status,
ship_ref->accelerate,
ship_ref->write_status.
2 抽象类和最终类
- 抽象类和抽象方法
有时一个基类包含多个派生类,但该基类只是作为模板出现的,并不需要有任何对象作为实例,则可以将该类声明为抽象类。
CLASS class DEFINITION ABSTRACT.
...
ENDCLASS.
抽象类不能使用CREATE OBJECT语句创建类对象,其主要目的是为了做派生类的模板,实际使用的对象则依照各个派生类创建。
抽象方法定义METHODS meth ABSTRACT ...
一个抽象方法不能在类本身中被实现,而必须在其派生出的非抽象类中实现。因而含有抽象方法的类必须被定义为抽象类。在派生类的实现过程中,即使用REDEFINITION关键字对该抽象方法进行重新定义,其唯一区别在于抽象方法重新定义过程中不能使用SUPER关键字调用该抽象方法。
- 最终类和最终方法
最终类,是不能被继承的类。
CLASS class DEFINITION FINAL.
...
ENDCLASS.
最终方法是不可以重新定义的方法,不一定出现在最终类中,但最终类的所有方法都是最终方法,因而无需指明FINAL关键字。METHODS meth ABSTRACT ...
一个最终类可以同时为抽象类,但此时类中只能包含静态成员。一个最终方法则不可以同时是抽象方法,因为这没有任何意义。
2 接口
1 接口的定义与实现
- 定义接口
语法格式
INTERFACE intf.
DATA ...
CLASS-DATA ...
METHOD ...
...
ENDINTERFACE.
在接口定义内部可以声明的成员与类中的成员相同(包括属性,方法和事件等),但无需著名具体的可见性,因为具体类中实现的所有接口组件成员均为共有成员。同时,接口定义中也只包含成员声明部分,而具体的实现也将在具体类中进行。
- 实现接口
接口没有自己的实例,因而也不需要进行方法实现,其中方法的实现要通过具体的类进行。在类中实现一个接口的语法格式如下
CLASS class DEFINITION.
PUBLIC SECTION.
...
INTERFACE: int1, int2.
...
ENDCLASS.
在类定义中,接口的实现只能出现在共有部分,接口中定义的所有组件都将被添加为该类的公有成员。接口intf中的成员icomp在类内部实现过程中以名称intf~icomp的形式出现。在类的实现部分,必须包含所有的接口方法实现。
CLASS class IMPLEMENTATION ...
...
METHOD intf1~imethl1.
...
ENDMETHOD.
...
METHOD intf2~imeth21.
...
ENDMETHOD.
...
ENDCLASS.
REPORT z_interface_demo.
INTERFACE status.
METHODS write.
ENDINTERFACE.
CLASS counter DEFINITION.
PUBLIC SECTION.
INTERFACES status.
METHODS increment.
PRIVATE SECTION.
DATA count TYPE i.
ENDCLASS.
CLASS counter IMPLEMENTATION.
METHOD status~write.
WRITE: / 'Count in counter is', count.
ENDMETHOD.
METHOD increment.
ADD 1 TO count.
ENDMETHOD.
ENDCLASS.
CLASS bicycle DEFINITION.
PUBLIC SECTION.
INTERFACES status.
METHODS drive.
PRIVATE SECTION.
DATA speed TYPE i.
ENDCLASS.
CLASS bicycle IMPLEMENTATION.
METHOD status~write.
WRITE: / 'Speed of bicycle is', speed.
ENDMETHOD.
METHOD drive.
ADD 10 TO speed.
ENDMETHOD.
ENDCLASS.
DATA: count TYPE REF TO counter,
bike TYPE REF TO bicycle,
status TYPE REF TO status,
status_tab TYPE TABLE OF REF TO status.
START-OF-SELECTION.
CREATE OBJECT: count, bike.
DO 5 TIMES.
CALL METHOD: count->increment,
bike->drive.
ENDDO.
APPEND: count TO status_tab,
bike TO status_tab.
LOOP AT status_tab INTO status.
CALL METHOD status->write.
ENDLOOP.
2 接口引用
接口引用也是一种对象引用,对于包含接口的类对象,除通过类引用访问之外,还可以通过接口引用进行访问。因为接口引用可以指向任何实现该接口的对象。
接口引用变量的定义与对象引用相似,通过TYPES或DATA语句中的TYPE REF TO 选项进行TYPE|DATA iref TYPE REF TO intf.
。其中intf是全局或程序中已经定义的接口。接口引用变量iref也可简称为接口引用。通过成员访问运算符“->”(iref->icomp),接口引用可以访问当前指向对象的接口中可见成员,其中的成员方法应该已经在对象所属的类中实现。
- 通过接口引用访问对象
要访问一个类对象,必须先声明一个基于该类的引用变量,如果该类为一个接口的实现,则可以将该引用变量赋值给一个接口变量,此时接口引用与类引用指向相同的类对象。假设接口变量名为iref,类引用名称为cref,语法格式如下iref = cerf.
如果该接口中包含实例属性attr和实例方法meth,则可以通过类引用变量或者接口引用变量两种方式对其进行访问。
使用类引用访问
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参照的类必须是接口iref的实现。
如果将接口引用赋值给对象引用cref = iref
则cref参照的类必须是ABAP预定于的空类OBJECT.
对于其他所有情况,则必须通过强制赋值语句或者强制赋值运算符"?="进行
MOVE iref? TO cref.
cref ?= iref.
在强制赋值时,系统不会出现任何静态语法检查,但系统将在运行时检查目标对象引用变量是否可以指向源变量引用的对象。如果允许则进行赋值,否则将触发可以捕获的运行时错误MOVE_CAST_ERROR。
3 事件
事件是除去属性,方法之外的第三种类成员。这三种组件中,属性用于刻画类对象的状态,方法用于访问或修改对象状态,而事件则用于一个类对象发布其状态的改变,因而其他对象可以捕获该方法并作出响应。
1 定义事件
必须首先在类声明部分声明事件,然后在其中的某个方法中触发事件
- 定义事件
事件可以在类或接口的声明部分进行声明。
EVEVTS|CLASS-EVENTS evt
EXPORTING ... VALUE(Ei) TYPE type [OPTIONAL] ...
声明事件时,可以使用EXPORTING附加项指定需要向事件处理方法中传递的参数,该参数传递方式恒为值传递。实例事件总是包含一个隐含的参数SENDER,该参数的类型为触发事件的类或接口的对象引用。
- 触发事件
一个实例事件可以被类中的任意方法触发,静态事件则可以被静态方法触发。
RAISE EVENT evt EXPORTING ... e1 = f1 ...
对于每一个没有被指定OPTIONAL项的形参e,均必须在实例方法中传递一个实参f,自身引用ME对象在被默认的传递至隐含参数SENDER.
2 事件处理方法
事件需要通过方法捕获事件并进行处理,必须首先为该事件定义一个事件处理方法(也可称之为事件处理器,然后在运行时为该事件进行注册)
- 声明事件处理方法
任何类都可以为其他类中定义的事件进行处理,同时也可以为自身事件进行处理。
METHODS|CLASS-METHODS
meth FOR EVENT evt OF cif IMPORTIONG ... e1 ...
一个事件处理方法的接口中只能包含定义在事件evt中的形参,并需遵循事件中的参数属性设定,但事件处理方法不需要使用RAISE EVENT中定义的参数。如果希望使用隐含参数SENDER,则必须将其列入参数接口列表中,该参数允许事件处理方法访问事件触发这,从而返回处理结果。
- 注册事件处理方法
要使事件处理方法能够对事件进行响应,必须在运行时为相关事件注册该方法SET HANDLER ... h ... [FOR] ...
该语句将处理方法和相关的触发方法进行链接,其语法和使用效果部分取决于事件的类型因此要区分下列四种类型事件- 定义在类中的实例事件
- 定义在接口中的实例事件
- 定义在类中的静态事件
- 定义在接口中的静态事件
对于实例事件,在进行处理之前必须在注册语句中使用附加项FOR指定希望注册到的对象。可以指定一个单独的对象作为事件触发者SET HANDLER ... h ... FOR ref.
或者使用FOR ALL INSTANCES附加项为处理器注册所有可以触发该事件的实例SET HANDLET ... h ... FOR ALL INSTANCES.
。该注册过程可以作用于目前尚未被创建的实例。
对于静态事件,则不能使用FOR附加项SET HANDLER ... h ...
该注册自动应用于整个类(或所有实现了包含该静态事件的接口类),对于接口中的静态事件,该注册可以应用于目前尚未装载至内存中的类(该类需是此接口的实现)
- 事件处理事件
在程序执行到RAISE EVENT语句之后,所有已注册的处理方法(可以有多个处理方法注册至同一事件)都将在下一个语句之前被处理(同步事件触发机制)。如果处理方法本身触发事件,则该处理方法在原方法继续执行之前被重新调用。
3 事件的触发及处理
在实际操作时,要使一个对象的事件处理方法能够捕获另一个对象触发事件,必须先使用SET HANDLER 语句进行注册,这个注册过程中形成了触发对象和响应对象之间的对应关系。当然,也可以同时注册多个处理对象与一个触发对象相对应。
SET HANDLER ... ref_handler->handler ...
FOR ref_sender | FOR ALL INSTANCES
[ACTIVATION act].
SET HANDLET 语句则创建一个系统内部的事件处理表,为每一个事件注册相应的方法。该表为系统隐含,用户不可见。对于实例事件,事件处理表中包含用于实现处理的方法名称以及注册了的类实例引用,该表通过SET HANDLER语句动态的进行维护。对应静态事件,系统为相关类创建一个实例无关的处理表。一旦事件被触发,系统将查找相关的事件表并执行对应于该事件的实例方法(或对应的静态方法).
REPORT z_class_counter_event.
CLASS vehicle DEFINITION.
PUBLIC SECTION.
EVENTS: too_fast exporting value(speed) type i.
METHODS: accelerate,
show_speed.
PROTECTED SECTION.
DATA speed TYPE i.
ENDCLASS.
CLASS vehicle IMPLEMENTATION.
METHOD accelerate.
speed = speed + 1.
IF speed > 5.
RAISE EVENT too_fast exporting speed = 5.
ENDIF.
ENDMETHOD.
METHOD show_speed.
WRITE: / 'Speed:', speed.
ENDMETHOD.
ENDCLASS.
CLASS handler DEFINITION.
PUBLIC SECTION.
METHODS handle_excess FOR EVENT too_fast OF vehicle importing speed.
ENDCLASS.
CLASS handler IMPLEMENTATION.
METHOD handle_excess.
WRITE: / 'Speed can not be too fast.', speed.
ENDMETHOD.
ENDCLASS.
DATA:o_vehicle TYPE REF TO vehicle,
o_handle TYPE REF TO handler.
START-OF-SELECTION.
CREATE OBJECT: o_vehicle, o_handle.
SET HANDLER o_handle->handle_excess FOR ALL INSTANCES.
DO 11 TIMES.
CALL METHOD o_vehicle->accelerate.
CALL METHOD o_vehicle->show_speed.
ENDDO.