Scala程序中如何實(shí)現(xiàn)多態(tài)和動態(tài)綁定
在10.4節(jié)中你看到了類型Element的變量可以指向類型ArrayElement的對象。這種現(xiàn)象的名字叫多態(tài):polymorphism,是指“許多形狀”或“許多形式”的意思。這種情況下,Element對象可以有許多形式。這種類型的多態(tài)被稱為子類型化多態(tài):subtyping polymorphism。Scala里另一種類型的多態(tài),稱為統(tǒng)一多態(tài):universal polymorphism,將在第19章討論。目前為止,你已經(jīng)看到了兩種形式:ArrayElement和LineElement。你可以通過定義新的Element子類創(chuàng)造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)
- }
圖釋 10.3 布局元素的類層級
51CTO編輯推薦:Scala編程語言專題
類Element的繼承層級現(xiàn)在看上去如圖釋10.3展示的樣子。結(jié)果,Scala將接受所有的下列賦值,因?yàn)橘x值表達(dá)式的類型符合定義的變量類型:
若你檢查繼承層級,你會發(fā)現(xiàn)這四個val定義的每一個里,等號右側(cè)表達(dá)式的類型都在將被初始化的等號左側(cè)的val類型之下。
- val e1: Element = new ArrayElement(Array("hello", "world"))
- val ae: ArrayElement = new LineElement("hello")
- val e2: Element = ae
- val e3: Element = new UniformElement('x', 2, 3)
然而,另一半的故事是,變量和表達(dá)式上的方法調(diào)用是動態(tài)綁定:dynamically bound的。這意味著被調(diào)用的實(shí)際方法實(shí)現(xiàn)取決于運(yùn)行期對象基于的類,而不是變量或表達(dá)式的類型。為了演示這種行為,我們會從我們的Element類中臨時移除所有存在的成員并添加一個名為demo的方法。我們會在ArrayElement和LineElement中重載demo,但UniformElement除外:
如果你把這些代碼輸入到了解釋器中,那么你就能定義這個帶了一個Element并調(diào)用demo的方法:
- 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
如果你傳給invokeDemo一個ArrayElement,你會看到一條消息指明ArrayElement的demo實(shí)現(xiàn)被調(diào)用,盡管被調(diào)用demo的變量e的類型是Element:
- def invokeDemo(e: Element) {
- e.demo()
- }
相同的,如果你傳遞LineElement給invokeDemo,你會看到一條指明LineElement的demo實(shí)現(xiàn)被調(diào)用的消息:
- scala> invokeDemo(new ArrayElement)
- ArrayElement's implementation invoked
傳遞UniformElement時的行為一眼看上去會有些可以,但是正確:
- scala> invokeDemo(new LineElement)
- LineElement's implementation invoked
- scala> invokeDemo(new UniformElement)
- Element's implementation invoked
因?yàn)閁niformElement沒有重載demo,它從它的超類Element繼承了demo的實(shí)現(xiàn)。因此,當(dāng)對象的類是UniformElement時,Element的實(shí)現(xiàn)就是要調(diào)用的demo的正確實(shí)現(xiàn)。
【相關(guān)閱讀】