Scala编程(第4版)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

10.4 扩展类

我们仍然需要有某种方式创建新的元素对象。你已经看到“new Element”是不能用的,因为Element类是抽象的。因此,要实例化一个元素,需要创建一个扩展自Element的子类,并实现contents这个抽象方法。示例10.3给出了一种可能的做法:

示例10.3 定义ArrayElement作为Element的子类

ArrayElement类被定义为扩展extend)自Element类。跟Java一样,可以在类名后面用extends子句来表达:

这样的extends子句有两个作用:它使得ArrayElement类从Element继承inherit)所有非私有的成员,并且它也让ArrayElement的类型成为Element类型的子类型subtype)。由于ArrayElement扩展自ElementArrayElement类被称作Element类的子类subclass)。反过来讲,ElementArrayElement超类superclass)。如果你去掉extends子句,Scala编译器会默认假定你的类扩展自scala.AnyRef,这对应到Java平台跟java.lang.Object相同。因此,Element类默认也扩展自AnyRef类。可以从图10.1中看到这些继承关系。

图10.1 ArrayElement的类图

继承inheritance)的意思是超类的所有成员也是子类的成员,但是有两个例外:一是超类的私有成员并不会被子类继承;二是如果子类里已经实现了相同名称和参数的成员,那么该成员不会被继承。对后面这种情况我们也说子类的成员重写override)了超类的成员。如果子类的成员是具体的而超类的成员是抽象的,我们也说这个具体的成员实现implement)了那个抽象的成员。

例如,ArrayElement里的contents方法重写(或者说实现)了Element类的抽象方法contents[2]与此不同的是,ArrayElement类从Element类继承了widthheight这两个方法。例如,假定有一个ArrayElement ae,可以用ae.width来查询其宽度,就像width是定义在ArrayElement类一样:

子类型subtying)的意思是子类的值可以被用在任何需要超类的值的场合。例如:

变量e的类型是Element,因此用于初始化它的值也应该是一个Element。事实上,初始值的类型是ArrayElement。这是可以的,因为ArrayElement类扩展自Element,这样,ArrayElement类型是与Element类型兼容的。[3]

图10.1还展示了ArrayElementArray[String]之间存在的组合composition)关系。这个关系被称为组合,因为ArrayElement用一个Array[String]组合出来,Scala编译器会在为ArrayElement生成的二进制类文件中放入一个指向传入的conts数组的字段。我们将在本章稍后的10.11节探讨关于组合和继承的设计考量点。