Scala开发教程(37): 组合和继承–多态和动态绑定

jerry Scala 2015年11月25日 收藏

在前面的例子我们看到类型为Element的变量可以保存ArrayElement类型的对象,这种现象称为“多态”。也就是基类类型的变量可以保存其子类类型的对象,到目前为止我们定义了两个Element的子类,ArrayElement和LineElement。你还可以定义其它子类,比如:

  1. class UniformElement (ch :Char,
  2. override val width:Int,
  3. override val height:Int
  4. ) extends Element{
  5. private val line=ch.toString * width
  6. def contents = Array.fill(height)(line)
  7. }

结合前面定义的类定义,我们就有了如下图所示的类层次关系:

20131120001

Scala将接受所有的下列赋值,因为赋值表达式的类型符合定义的变量类型:

  1. val e1: Element = new ArrayElement(Array("hello", "world"))
  2. val ae: ArrayElement = new LineElement("hello")
  3. val e2: Element = ae val
  4. e3: Element = new UniformElement('x', 2, 3)

若你检查继承层次关系,你会发现这四个val定义的每一个表达式,等号右侧表达式的类型都在将被初始化的等号左侧的val类型的层次之下。
另一方面,如果调用变量(对象)的方法或成员变量,这个过程是一个动态绑定的过程,也就是说调用哪个类型的方法取决于运行时变量当前的类型,而不是定义变量的类型。
为了显示这种行为,我们在Element中添加一个demo方法,定义如下:

  1. abstract class Element {
  2. def demo() {
  3. println("Element's implementation invoked")
  4. }
  5. }
  6.  
  7. class ArrayElement extends Element {
  8. override def demo() {
  9. println("ArrayElement's implementation invoked")
  10. }
  11. }
  12.  
  13. class LineElement extends ArrayElement {
  14. override def demo() {
  15. println("LineElement's implementation invoked")
  16. }
  17.  
  18. }
  19.  
  20. // UniformElement inherits Element’s demo
  21. class UniformElement extends Element
  22.  

如果你使用交互式Scala解释器来测试,你可以定义如下的方法:

  1. def invokeDemo(e: Element) {
  2. e.demo()
  3. }
  4.  

下面我们分别使用ArrayElement, LineElement和UniformElement来调用这个方法:

  1. scala> invokeDemo(new ArrayElement)
  2. ArrayElement's implementation invoked
  3.  
  4. scala> invokeDemo(new LineElement)
  5. LineElement's implementation invoked
  6.  
  7. scala> invokeDemo(new UniformElement)
  8. Element's implementation invoked
  9.  

可以看到由于ArrayElement和LineElement重载了Element的demo方法,因此调用invokeDemo时由于“动态绑定”因此会调用这些子类的demo方法,而由于UniformElement没有重载Element的demo方法,动态绑定时也会调用UniformElement的demo方法(但此时实际为基类的demo方法)