Scala程序中如何实现多态和动态绑定

开发 后端
本文节选自Martin Odersky,Lex Spoon和Bill Venners所著,Regular翻译的《Programming in Scala》的第十章。Scala是一种针对 JVM 将函数和面向对象技术组合在一起的编程语言。

在10.4节中你看到了类型Element的变量可以指向类型ArrayElement的对象。这种现象的名字叫多态:polymorphism,是指“许多形状”或“许多形式”的意思。这种情况下,Element对象可以有许多形式。这种类型的多态被称为子类型化多态:subtyping polymorphism。Scala里另一种类型的多态,称为统一多态:universal polymorphism,将在第19章讨论。目前为止,你已经看到了两种形式:ArrayElement和LineElement。你可以通过定义新的Element子类创造Element的更多形式。例如,下面定义了拥有给定长度和高度并被指定字符充满的新的Element形式:

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

布局元素的类层级

图释 10.3 布局元素的类层级

51CTO编辑推荐:Scala编程语言专题

类Element的继承层级现在看上去如图释10.3展示的样子。结果,Scala将接受所有的下列赋值,因为赋值表达式的类型符合定义的变量类型:

val e1: Element = new ArrayElement(Array("hello""world"))  
val ae: ArrayElement = new LineElement("hello")  
val e2: Element = ae  
val e3: Element = new UniformElement('x'23)  
  • 1.
  • 2.
  • 3.
  • 4.
若你检查继承层级,你会发现这四个val定义的每一个里,等号右侧表达式的类型都在将被初始化的等号左侧的val类型之下。

然而,另一半的故事是,变量和表达式上的方法调用是动态绑定:dynamically bound的。这意味着被调用的实际方法实现取决于运行期对象基于的类,而不是变量或表达式的类型。为了演示这种行为,我们会从我们的Element类中临时移除所有存在的成员并添加一个名为demo的方法。我们会在ArrayElement和LineElement中重载demo,但UniformElement除外:

abstract class Element {  
 def demo() {  
  println("Element's implementation invoked")  
 }  
}  
class ArrayElement extends Element {  
 override def demo() {  
  println("ArrayElement's implementation invoked")  
 }  
}  
class LineElement extends ArrayElement {  
 override def demo() {  
  println("LineElement's implementation invoked")  
 }  
}  
// UniformElement inherits Element’s demo  
class UniformElement extends Element  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
如果你把这些代码输入到了解释器中,那么你就能定义这个带了一个Element并调用demo的方法:

def invokeDemo(e: Element) {  
 e.demo()  
}  
  • 1.
  • 2.
  • 3.
如果你传给invokeDemo一个ArrayElement,你会看到一条消息指明ArrayElement的demo实现被调用,尽管被调用demo的变量e的类型是Element:

scala> invokeDemo(new ArrayElement)  
ArrayElement's implementation invoked  
  • 1.
  • 2.
相同的,如果你传递LineElement给invokeDemo,你会看到一条指明LineElement的demo实现被调用的消息:

scala> invokeDemo(new LineElement)  
LineElement's implementation invoked  
  • 1.
  • 2.
传递UniformElement时的行为一眼看上去会有些可以,但是正确:

scala> invokeDemo(new UniformElement)  
Element's implementation invoked  
  • 1.
  • 2.

因为UniformElement没有重载demo,它从它的超类Element继承了demo的实现。因此,当对象的类是UniformElement时,Element的实现就是要调用的demo的正确实现。

【相关阅读】

  1. Scala学习:调用超类构造器和override修饰符的使用
  2. Scala:重载方法和字段及定义参数化字段
  3. Scala程序中的扩展类
  4. 在Scala中定义无参数方法
  5. 学习Scala的二维布局库和抽象类

责任编辑:book05 来源: Artima
相关推荐

2011-08-22 09:34:50

Objective-C多态动态类型

2014-12-29 10:19:01

Java

2023-10-20 09:51:00

编程开发

2009-07-22 09:08:09

Scalaabove方法

2009-07-20 18:03:26

Scala程序Singleton对象

2009-07-22 07:53:00

Scala扩展类

2009-07-22 09:27:04

Scala变高变宽

2021-12-12 20:10:49

域名动态IP

2011-04-12 10:40:04

C++多态

2009-07-22 09:43:30

Scala类型

2012-07-25 14:25:08

PrismPrism4MVVM

2020-08-20 08:23:52

VueDOM运算符

2012-01-09 11:26:15

Java

2021-06-30 10:32:33

反射多态Java

2010-09-14 09:30:04

Java多态

2017-10-27 22:03:35

javascrip

2009-07-21 07:30:00

Scala程序Application

2010-03-05 16:56:42

Python绑定C++

2020-06-19 12:59:33

动态脚本Java

2011-07-13 15:32:38

ODBC数据库驱动程序系统DSN
点赞
收藏

51CTO技术栈公众号