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键会弹出一个简洁的帮助对话框。这就是同一个操作在不同对象上不同的反应。
多态可以提升代码的可扩展性,采用了基于多态的一些设计模式,可以让我们在少量修改甚至不修改原有代码的基础上,轻松加入新的功能,使代码更加健壮,易于维护。
实现多态有三个前提条件:
- 多态发生在有继承关系的类之间(继承的主要目的也是为了多态)
- 子类要对父类方法进行重写覆盖(重写覆盖就是功能细分的实现)
- 父类引用指向子类对象(向上转型Upcasting,就是多态的标志)
我们逐一解释着几个条件:
- 多态必须要有继承关系
多态是不同的对象执行同一个方法时,具有多个不同表现形式的能力,使用不同的实例,针对同一个方法执行不同的操作。"不同对象具有同一个方法",最为方便的实现方式就是多个类从一个类中继承,子类们可以自动继承父类所定义的公有和受保护的方法。
- 子类要对父类方法进行覆盖重定义
覆盖就是子类对父类的方法进行修改和覆盖,执行不同的逻辑。
面向对象语言中(如C++)有覆盖和重载两种概念,在ABAP中只有重定义(Redefine)这个概念,相当于覆盖这个概念。
覆盖(Overloading),是指子类重新定义父类的虚方法的做法,ABAP中叫做重新定义(Redefine)。
重载(Overriding),是指同一个类允许存在多个同名方法,而这些方法的参数表不同(参数个数不同,或参数类型不同),而SAP ABAP的同一个类里面,同名方法是不允许出现的,也就不存在重载。
所以,我们ABAP的多态,也就是覆盖和重定义(Overloading/Redefine)这种实现方式。也就是子类对继承得来的方法进行改造,使得该方法符合子类逻辑的过程。
-
子类要向上转型为父类对象,执行多态。
子类对象赋值给基类对象,把子类对象作为基类对象使用,称为(Upcasting)"向上转型"。向上转型就是将基类类型变量指向子类的对象的过程,使得父类可以访问子类方法,但基类对象不能访问子类对象新增加的属性和方法。
向上转型的方法如下:
设定父类对象为go_superj,子类对象为go_sub。
- 如果父类是可创建对象实例的类,或者抽象类和接口,可以按子类类型创建父类对象,如:
DATA: go_super TYPE REF TO zcl_super.
CREATE OBJECT go_super TYPE zcl_sub.
- 如果父类是可创建对象实例的类,分别创建子类和父类对象后,采用一般赋值语句"=", 如:go_super = go_sub.
- 如果父类是可创建对象实例的类,分别创建子类和父类对象后,可以使用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. |
此外,还有一个概念是向下转型,就是父类对象强制赋值给子类对象,把父类对象当做子类对象来使用称为"强制向下转型"(downcasting)。向下强转型(downcasting),主要目的是为了能够让父类对象调用子类特定的方法。
向上转型是从子类转型到父类,而向下转型是父类转型到子类。上转型是任何时候都允许进行的,而向下转型则会有类型检查,如果不匹配会报出类型错误。
向下转型的方法如下:
设定父类对象为go_super,子类对象为go_sub,子类的类为zcl_sub。
-
如果子类经过向上转型到父类,则父类对象可以直接向下强转型。
向下强转型,采用 "?="进行赋值,要将父类对象赋值给子类对象,这个转换是强制的,lo_sub ?= lo_super.
- 向下强转型的另一种格式是MOVE ?TO,需要在TO之前加一个问号,以示强制转型: MOVE lo_super ?TO lo_sub.
-
也可采用SAP的关键字CAST进行向下强转型,格式为"CAST 子类类型(父类对象)",如:CAST zcl_sub( go_super ).
- 向下强转型前,如果父类对象没有经过子类的向上转型,直接进行向下强转型,会引发错误。
可以先向上转型,如果父类是不能创建实例的抽象类或接口,可以参照向上转型的第一种方法,将对应的父类对象创建时采用子类类型创建。
如: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. |
如图5-38所示,前三个下转型都可以成功