5.4 SAP ABAP 面向对象概念 - 多态 - 摘自 《SAP ABAP面向对象程序设计:原则、模式及实践》

本文详细介绍了SAP ABAP中的多态概念,包括多态的生物学和化学起源,以及在面向对象编程中的应用。多态是通过父类引用调用子类方法实现的,允许不同对象对同一操作有不同的响应。文章通过实例展示了多态的实现,包括基于非抽象类和抽象类,并强调了多态在提高代码可扩展性和维护性方面的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

5.4 多态

5.4.1 多态的概述

多态是一个生物学和化学中的专有名词,被面计算机科学家引用到面向对象的程序设计中。

我们用柯林斯英语词典看一下多态一词的原始意义:

polymorphism:n.    多型现象,多态性; 多机组合形式; 多形性;

1. Biology - the occurrence of more than one form of individual in a single species within an interbreeding population

2. The existence or formation of different types of crystal of the same chemical compound

 

1. 生物学 - 在单一的种群中出现的多种形式的个体的现象,同一物种形态构造表现为多种不同的个体,如蜜蜂的不同个体,有蜂王,雄蜂和工蜂。

2. 化学 - 同素异形体,同一化合物可以形成的不同类型的形态,如碳的同素异形体有金刚石和石墨等。

 

多态(Polymorphism)的意义是"一个实体同时具有多种形态"。多态是面向对象程序设计(OOP)的一个重要特征。在面向对象的程序中,多态(Polymorphism)指的是对不同的对象执行同一个操作,程序可以有不同的解释,执行不同的逻辑,返回不同的执行结果。

在面向对象的程序中,多态是基于父类和子类的继承,通过指向父类的对象,调用不同子类对象的方法来完成的。

 

现实中类似例子,如在Windows系统中,F1键被定义为"显示帮助"按钮,但在不同的软件系统中,帮助的显示和弹出方法又都是不同的。

如果我们正在操作的是WPS文字软件,按F1键会在右侧弹出"帮助与问答界面"。如果正在使用Chrome浏览器,按F1键会打开一个新的页面,显示support.google.com页面。如果使用 iTunes软件,按F1键会弹出一个简洁的帮助对话框。这就是同一个操作在不同对象上不同的反应。

 

多态可以提升代码的可扩展性,采用了基于多态的一些设计模式,可以让我们在少量修改甚至不修改原有代码的基础上,轻松加入新的功能,使代码更加健壮,易于维护。

 

实现多态有三个前提条件:

  1. 多态发生在有继承关系的类之间(继承的主要目的也是为了多态)
  2. 子类要对父类方法进行重写覆盖(重写覆盖就是功能细分的实现)
  3. 父类引用指向子类对象(向上转型Upcasting,就是多态的标志)

 

我们逐一解释着几个条件:

  1. 多态必须要有继承关系

多态是不同的对象执行同一个方法时,具有多个不同表现形式的能力,使用不同的实例,针对同一个方法执行不同的操作。"不同对象具有同一个方法",最为方便的实现方式就是多个类从一个类中继承,子类们可以自动继承父类所定义的公有和受保护的方法。

 

  1. 子类要对父类方法进行覆盖重定义

覆盖就是子类对父类的方法进行修改和覆盖,执行不同的逻辑。

面向对象语言中(如C++)有覆盖和重载两种概念,在ABAP中只有重定义(Redefine)这个概念,相当于覆盖这个概念。

覆盖(Overloading),是指子类重新定义父类的虚方法的做法,ABAP中叫做重新定义(Redefine)。

重载(Overriding),是指同一个类允许存在多个同名方法,而这些方法的参数表不同(参数个数不同,或参数类型不同),而SAP ABAP的同一个类里面,同名方法是不允许出现的,也就不存在重载。

所以,我们ABAP的多态,也就是覆盖和重定义(Overloading/Redefine)这种实现方式。也就是子类对继承得来的方法进行改造,使得该方法符合子类逻辑的过程。

 

  1. 子类要向上转型为父类对象,执行多态。

    子类对象赋值给基类对象,把子类对象作为基类对象使用,称为(Upcasting)"向上转型"。向上转型就是将基类类型变量指向子类的对象的过程,使得父类可以访问子类方法,但基类对象不能访问子类对象新增加的属性和方法。

 

向上转型的方法如下:

设定父类对象为go_superj,子类对象为go_sub。

  1. 如果父类是可创建对象实例的类,或者抽象类和接口,可以按子类类型创建父类对象,如:

DATA: go_super TYPE REF TO zcl_super.

CREATE OBJECT go_super TYPE zcl_sub.

  1. 如果父类是可创建对象实例的类,分别创建子类和父类对象后,采用一般赋值语句"=", 如:go_super = go_sub.
  2. 如果父类是可创建对象实例的类,分别创建子类和父类对象后,可以使用MOVE TO:MOVE go_sub TO go_super.

 

举个现实中的例子,我们常见的"水果",可以定义为父类,其子类就有苹果类,梨子类等。

如"苹果"是可以向上转型为"水果"的,因为苹果就是一种水果,只要对象符合"IS-A"的关系(apple is a kind of fruit),就可以向上转型。

比如我拿着一只食堂领的苹果,问同事"看我的水果如何?"(这个问题就是将具体的"苹果"上转型为抽象的概念"水果"了),他们会漫不经心地回答,挺新鲜的。(苹果是一种水果,上转型是有意义的。如果我拿着一只梨子问同样的问题,效果也是一样的,符合IS-A的关系的上转型都是有意义的)。

如果我拿一部iPhone手机,问同事"看我的水果如何?",大家会觉得我在说冷笑话。(有人叫苹果公司的iPhone为水果机,但毕竟iPhone不是水果,不符合IS-A的关系,此时上转型是无意义的,所以会让人无法理解)。并且,试图对非继承关系的类进行强制转型也会引起"The type of "GO_XXX" cannot be converted to the type of "GO_XXX" 的转型错误。

 

如示例程序5.14所示,向上转型可以实现如下。

"示例程序5.14

REPORT zrep_cls_0151.

"父类水果,从ABAP根类object继承
CLASS
zcl_fruit DEFINITION INHERITING FROM object.
ENDCLASS.
"子类苹果,继承自水果类
CLASS
zcl_apple DEFINITION INHERITING FROM zcl_fruit.
ENDCLASS.
"子类梨子,继承自水果类
CLASS
zcl_pear DEFINITION INHERITING FROM zcl_fruit.
ENDCLASS.
"定义类对象
DATA go_fruit TYPE REF TO zcl_fruit.
DATA go_apple TYPE REF TO zcl_apple.
DATA go_pear TYPE REF TO zcl_pear.

"第1种上转型,按子类类型创建父类对象
"父类是可创建对象实例的类,或者抽象类和接口
CREATE OBJECT go_fruit TYPE zcl_apple.
"或者使用NEW语句
go_fruit = NEW zcl_apple( ).

"第2种上转型,如果父类是可创建对象实例的类,
"分别创建子类和父类对象后,采用一般赋值语句
CREATE OBJECT go_fruit.
CREATE OBJECT go_apple.
go_fruit = go_apple.

"第3种上转型,如果父类是可创建对象实例的类,
"分别创建子类和父类对象后,采用MOVE TO 语句
CREATE OBJECT go_fruit.
CREATE OBJECT go_apple.
MOVE go_apple TO go_fruit.

 

  

 

此外,还有一个概念是向下转型,就是父类对象强制赋值给子类对象,把父类对象当做子类对象来使用称为"强制向下转型"(downcasting)。向下强转型(downcasting),主要目的是为了能够让父类对象调用子类特定的方法。

向上转型是从子类转型到父类,而向下转型是父类转型到子类。上转型是任何时候都允许进行的,而向下转型则会有类型检查,如果不匹配会报出类型错误。

 

向下转型的方法如下:

 

设定父类对象为go_super,子类对象为go_sub,子类的类为zcl_sub。

  1. 如果子类经过向上转型到父类,则父类对象可以直接向下强转型。

    向下强转型,采用 "?="进行赋值,要将父类对象赋值给子类对象,这个转换是强制的,lo_sub ?= lo_super.

 

  1. 向下强转型的另一种格式是MOVE ?TO,需要在TO之前加一个问号,以示强制转型: MOVE lo_super ?TO lo_sub.

 

  1. 也可采用SAP的关键字CAST进行向下强转型,格式为"CAST 子类类型(父类对象)",如:CAST zcl_sub( go_super ).

     

  2. 向下强转型前,如果父类对象没有经过子类的向上转型,直接进行向下强转型,会引发错误。

 

可以先向上转型,如果父类是不能创建实例的抽象类或接口,可以参照向上转型的第一种方法,将对应的父类对象创建时采用子类类型创建。

如:CREATE OBJECT lo_super TYPE zcl_sub_class.

然后再进行下转型。

 

试图用父类型创建出子类型对象的"向下强转型"是不可行的,如:CREATE OBJECT go_apple TYPE zcl_fruit.

会报出"The specified type cannot be converted into the target variables."错误。

 

如示例程序5.15所示,下转型是需要类型验证的,如"苹果"和"梨子"上转型为"水果"后,可以下转型为"苹果"或者"梨子"。在ABAP 7.5 版本后,可以使用 IS INSTANCE OF 操作符来判断具体的类对象的类型,对具体类型进行验证。

"示例程序5.15

REPORT zrep_cls_015.
定义素食类
CLASS
zcl_vegetarian_food DEFINITION.
ENDCLASS.
定义水果类,继承自素食类
CLASS
zcl_fruit DEFINITION INHERITING FROM zcl_vegetarian_food.
ENDCLASS.

定义苹果类,继承自水果类
CLASS
zcl_apple DEFINITION INHERITING FROM zcl_fruit.
ENDCLASS.

定义梨子类,继承自水果类
CLASS
zcl_pear DEFINITION INHERITING FROM zcl_fruit.
ENDCLASS.
定义类变量
DATA go_vegetarian_food TYPE REF TO zcl_vegetarian_food.
DATA go_fruit TYPE REF TO zcl_fruit.
DATA go_apple TYPE REF TO zcl_apple.
DATA go_pear TYPE REF TO zcl_fruit.


"第1种向下转型,如果子类经过向上转型到父类,则父类对象可以直接向下强转型。
"向下强转型,采用 "?="进行赋值

先创建对象
CREATE OBJECT go_vegetarian_food TYPE zcl_vegetarian_food .
CREATE OBJECT go_apple TYPE zcl_apple.

向上转型
go_vegetarian_food = go_apple.

然后用"?="下转型
TRY.
go_apple ?= go_vegetarian_food.
WRITE: / 'go_apple ?= go_vegetarian_food - Correct'.
CATCH cx_sy_move_cast_error.
WRITE: / 'go_apple ?= go_vegetarian_food - Error'.
ENDTRY.

"第2种向下转型,如果子类经过向上转型到父类,则父类对象可以直接向下强转型
"向下强转型,采用 "MOVE ?TO"进行赋值

先创建对象
CREATE OBJECT go_vegetarian_food TYPE zcl_vegetarian_food .
CREATE OBJECT go_pear TYPE zcl_pear.

向上转型
go_vegetarian_food = go_pear.

然后用"MOVE ?TO"下转型
TRY.
MOVE go_vegetarian_food ?TO go_pear.
WRITE: / 'MOVE go_vegetarian_food ?TO go_pear - Correct'.
CATCH cx_sy_move_cast_error.
WRITE: / 'MOVE go_vegetarian_food ?TO go_pear - Error'.
ENDTRY.

"第3种向下转型,如果子类经过向上转型到父类,则父类对象可以直接向下强转型
"向下强转型,采用 "CAST"进行赋值

先创建对象
CREATE OBJECT go_vegetarian_food TYPE zcl_vegetarian_food.
CREATE OBJECT go_pear TYPE zcl_pear.

向上转型
go_vegetarian_food = go_pear.

然后用"CAST"下转型
TRY.
CAST zcl_pear( go_vegetarian_food ).
WRITE: / 'CAST zcl_pear( go_vegetarian_food ) - Correct'.
CATCH cx_sy_move_cast_error.
WRITE: / 'CAST zcl_pear( go_vegetarian_food ) - Error'.
ENDTRY.


"第4种向下转型,如果父类对象没有经过子类的向上转型,直接进行向下强转型,会引发错误。

先创建对象
CREATE OBJECT go_vegetarian_food TYPE zcl_vegetarian_food.
CREATE OBJECT go_pear TYPE zcl_pear.

没有经过子类的向上转型
"go_vegetarian_food = go_pear.

此时用"CAST"下转型,会引发错误。
TRY.
CAST zcl_pear( go_vegetarian_food ).
WRITE: / 'CAST zcl_pear( go_vegetarian_food ) - Correct'.
CATCH cx_sy_move_cast_error.
WRITE: / 'CAST zcl_pear( go_vegetarian_food ) - Error'.
ENDTRY.

  

 

如图5-38所示,前三个下转型都可以成功

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值