多态是面向对象的核心特性。一说ADA即使95也没有很好地支持多态性。这可能并不是其一个显著缺陷,而是设计者认为多态性不一定能在ADA语言的通常应用模式中能很好地发挥。但毕竟多态性是很重要的一种属性。好在2005版本做了一些改进,其关键是引入了class-wide类型,即在基类上调'Class记号(Attribute)产生的类型。由于ADA主张常用静态的对象,所以这种情形下的多态看上去有点奇怪;另一方面多态往往和指针引用有很大关系,ADA的指针对对象和指针的声明和类型指定都很严格,所以用起来不像其他语言那么方便灵活。
以下示例基本演示这个特性,并一些其他的语言特性和编程要点:
1. 首先是定义基类的包的声明(person.ads),注意:
- ADA的语言特征是以包作为标识对象(数据+操作)封装的单位,因此以下的这个基类person.object,不应将person像在其他面向对象语言中那样看成名空间,而应当看作这种封装概念的一部分;
- ADA一个数据封装单位即record或者是完全可见的(相当于在public)或是定义在private区域中成为完全不可见的。
package person is type gender_type is (female, male); type object is tagged private; procedure set_name (o : in out object; name : string); procedure set_gender (o : in out object; gender : gender_type); procedure put(o : in object); private -- private region -- encapsulated object data (tagged as it's object oriented) type object is tagged record name : string(1..10); gender : gender_type; end record; end person;
with Ada.Text_IO; package body person is procedure set_name (o : in out object; name : string) is begin o.name := name; end; procedure set_gender (o : in out object; gender : gender_type) is begin o.gender := gender; end; procedure put(o : in object) is begin Ada.Text_IO.Put(o.name); Ada.Text_IO.Put(" is a "); Ada.Text_IO.Put_Line(Gender_Type'Image(o.gender)); end put; end person;
3. 再是子类的声明(programmer.ads),可见其中的object(同名无妨,而用package名区分,再次说明package是表征对象的标识)指明继承person.object。这里还显示了一个嵌套定义特性(ADA类似PASCAL语言,可以在任意可定义的位置嵌套定义各种程序内容);枚举(enumeration)和C/C++一样,语法上类似不将类型名作为限定符的常量,所以要避免污染,比如这里的第一个元素adabyron,如果定义成ada,就会影响其后的ada.containers...的编译。
with person; with Ada.Containers.Ordered_Sets; package programmer is -- extends object in original package to what it is as a programmer type object is new person.object with private; -- nested package for putting languages and related functions together package languages is type name is (adabyron, algol, basic, c, cobol, cplusplus, csharp, delphi, fortran, java, javascript, lisp, pascal, perl, prolog, python, ruby); package sets is new Ada.Containers.Ordered_Sets(languages.name); end languages; procedure set_skills (o : in out object; langs : languages.sets.set); procedure add_skill (o : in out object; lang : languages.name); overriding -- optional, new in Ada 2005; the same when omitted in this case procedure put (o : in object); private type object is new person.object with record skilled_in : languages.sets.set; end record; end programmer;
4. 子类实现(programmer.adb),这里的put事实上重载了基类的对应方法(在上述声明中可用overriding标识,ADA 2005特性)。由于ADA是将对象数据作为第一参数的,所以不存在this指针,只需要对这个第一参数进行操作即可。而如果要调用基类方法,则只要显示地到基类的包里去找那个方法即可,而当前类型的对象数据要转换成基类对象数据(被称作视图转换,view conversion,没有额外开销)填入到其第一参数中去。这里也演示了Ada库中的Set的用法,这里的Cursor只能显式使用了。具体的原理目前还不很清楚。
with Ada.Text_IO; package body programmer is procedure set_skills (o : in out object; langs : languages.sets.set) is begin o.skilled_in := langs; end set_skills; procedure add_skill (o : in out object; lang : languages.name) is begin o.skilled_in.Insert(lang); end add_skill; procedure put (o : in object) is lang : languages.name; lang_cur : languages.sets.Cursor; begin -- make a view conversion to base type and put it person.put(person.object(o)); Ada.Text_IO.Put(" who is skilled in "); lang_cur := languages.sets.First(o.skilled_in); while languages.sets.Has_Element(lang_cur) loop lang := languages.sets.element(lang_cur); Ada.Text_IO.Put(languages.name'Image(lang)); Ada.Text_IO.Put(" "); lang_cur := languages.sets.Next(lang_cur); end loop; Ada.Text_IO.Put_Line(""); end put; end programmer;
with person; with programmer; procedure Main is -- these two objects need to be declared aliased to be able to be -- referenced by local access objects (ADA pointers) someone : aliased person.object; me : aliased programmer.object; langs : programmer.languages.sets.Set; begin someone.set_name("Minnie "); someone.set_gender(person.female); someone.put; me.set_name("Lincoln "); me.set_gender(person.male); declare -- for language loading use programmer.languages; begin langs.Insert(adabyron); langs.Insert(basic); langs.Insert(cobol); langs.Insert(c); langs.Insert(cplusplus); langs.Insert(csharp); me.set_skills(langs); me.add_skill(pascal); me.add_skill(javascript); me.add_skill(delphi); me.add_skill(java); end; me.put; declare -- access type to class-wide type for person family -- it has to be declared locally to be able to reference local variabless type person_access is access all person.object'Class; perclass : person.object'Class := me; peracc : person_access; procedure display(o : person.object'Class) is begin o.put; end display; begin perclass.put; -- use pre-assigned class-wide-type object peracc := me'Access; peracc.put; -- set a class-wide-type pointer to an object of child type peracc := someone'Access; peracc.put; -- set the point to an object of base type display(me); -- pass an object to a method with class-wide-type parameter end; end Main;
Minnie is a FEMALE
Lincoln is a MALE
who is skilled in ADABYRON BASIC C COBOL CPLUSPLUS CSHARP DELPHI JAVA JAVASCRIPT PASCAL
Lincoln is a MALE
who is skilled in ADABYRON BASIC C COBOL CPLUSPLUS CSHARP DELPHI JAVA JAVASCRIPT PASCAL
Lincoln is a MALE
who is skilled in ADABYRON BASIC C COBOL CPLUSPLUS CSHARP DELPHI JAVA JAVASCRIPT PASCAL
Minnie is a FEMALE
Lincoln is a MALE
who is skilled in ADABYRON BASIC C COBOL CPLUSPLUS CSHARP DELPHI JAVA JAVASCRIPT PASCAL