1. 程式人生 > >Scala繼承與Java的區別

Scala繼承與Java的區別

在之前的筆記Java靜態屬性和方法的繼承問題中,通過具體的實驗證明,在子類中重寫父類的欄位時並沒有覆蓋父類的欄位,只是隱藏了父類的欄位。而在scala中則不同,scala子類的同名欄位會重寫且覆蓋父類的同名欄位,這裡做了個簡單實驗,並記錄下來。

Parent.scala

class Parent {
  val normalStr: String = "Normal member of parent."

  def normalMethod() = {
    println("Normal method of parent.")
  }
}

定義了一個欄位normalStr和一個方法normalMethod()

,在Scala中,類的欄位是由一個私有屬性和對應的getter/setter方法組成的。

Child.scala

class Child extends Parent {
  override val normalStr: String = "Normal member of child."

  override def normalMethod() = {
    println("Normal method of child.")
  }
}

子類Child繼承了父類Parent,並override父類的normalStrnormalMethod()

TestMain和Result

object TestMain{
  def main(args: Array[String]) {
    val child: Child = new Child
    println(child.normalStr)
    child.normalMethod()

    //val child1:Parent = child.asInstanceOf[Parent]
    //採用Parent型別的變數指向建立的Child物件
    val child1:Parent = new Child
    println(child1.normalStr)
    child1.normalMethod()
  }
}

輸出的結果如下:

Normal member of child.
Normal method of child.
Normal member of child.
Normal method of child.

從結果可以看出,子類重寫並覆蓋了父類的同名屬性和方法

Scala子類的構造順序

這裡順便記錄下Scala子類的構造順序,這裡直接用書上給出的例子,以便後續檢視:

先寫兩個類,一個父類Creature.scala,一個子類Ant.scala:

Creature

class Creature {
    val range: Int = 10
    val env: Array[Int] = new Array[Int](range)
    def show(): Unit = {
       println(range)
    }
}

Ant

class Ant extends Creature {
    override val range = 2
}

現在建立一個Ant的物件ant,那麼ant.env.length的值是多少,憑第一感覺應該是10或者2,然而答案是0,接下來我寫下ant建立的過程中構造器的執行順序:

  1. 首先呼叫父類Creature的構造器(父類的構造器先於子類的構造器被呼叫),所以首先把range設定為10。為了後續的說明這裡說明下,類的欄位是由一個私有屬性和對應的gettersetter方法組成的,而子類在重寫父類的同名欄位時,對於val型別的屬性子類重寫了getter方法。
  2. 接下來初始化env陣列,所以需要呼叫rangegetter方法,然而子類已經重寫了getter方法,且子類並沒初始化,所有的欄位都是物件建立過程中,記憶體清零後的預設值,所以此時range的值為0。這也就解釋了上述問題的疑問。
  3. 接下來呼叫子類的構造器,range被設為2。

所以在構造器中,物件的初始化不應該依賴於val的值,因為val的值對應的getter方法可能會被子類重寫覆蓋。解決辦法有:

  1. val宣告為final。(簡單高效,但是不夠靈活)
  2. 在超類中將val宣告為lazy。(簡單靈活,但是不夠高效)
  3. 還有種就是子類中使用提前定義語法。(這個就不介紹了)

ant物件呼叫show()方法輸出的則是子類range的值,即為2。而在Java中,則是父類的range的值:10。主要原因還是由於在Scala中,子類重寫父類的屬性或者方法,覆蓋了父類的屬性和方法,而在Java中,只有非靜態的方法會被子類重寫覆蓋,而非靜態/靜態屬性和靜態方法都只是被隱藏了。

主要參考:《快學Scala》