2011年5月18日 星期三

5-14. Method override

如之前篇幅所提,Scala method 宣告與 Java 精神類似。但使用上有點不太一樣,我們一一說明

一. method override 需要清楚指明

Java 使用 @Override annotation 來指明要 override。使用 @Override 的好處是,你可以避免 override 了錯誤的  method,或是打錯字。建議你在使用 Java 時,需要善用 @Override。

Scala 承繼這個精神,並且發揚光大。在 Scala 中,若是你要 override method,需要特別指明,指明的方式,就是在 method 宣告時,使用 override 這個 keyword。
1. 若沒有指明 override,但你宣告一個與 parent class 相同的 method,compiler 會要求你確認是要 create 一個新的  method,還是要 override parent 的 method。
2. 若使用 override ,但祖先 class 並沒有宣告相同的 method,表示你的 override,是有問題的,compiler 會要求你修正正確。
範例:override
class S1 {
  def m1() = 10
}
class S2 extends S1 { // 繼承 S1
  override def m1() = 20 // 明確說明 override m1
}
範例:override 錯誤的 method
class S1 {
  def m1() = 10
}
class S2 extends S1 { // 繼承 S1
  override def m2() = 20 // 明確說明 override m2,但 S1 沒有 m2,所以 compile error
}

範例:沒有 override,但 method 與 parent class 相同
class S1 {
  def m1() = 10
}
class S2 extends S1 { // 繼承 S1
  def m1() = 20 // m1 在 parent class 出現過,所以此處會有 compile error
}

二. abstract method 可以不需宣告 override
前面有篇幅提過 abstract method,就是沒有 implement 的 method。
若你的 method 是 override abstract method,此時可以宣告 override,也可不宣告 override
範例:abstract method,可以不需宣告 override
abstract class S1 {
  def m1() //m1 是 abstract method,因為沒有 method body
}
class S2 extends S1 { // 繼承 S1  
  def m1() = 20 // m1 在 parent class 出現過,但因為是 abstract method,所以可以不宣告 override
}
範例:abstract method,也可以宣告 override
abstract class S1 {
  def m1() //m1 是 abstract method,因為沒有 method body
}
class S2 extends S1 { // 繼承 S1  
  override def m1() = 20 // m1 在 parent class 出現過,但因為是 abstract method,也可宣告 override
}

三. 「空括號 method」與「無括號 method」可以互相 override
雖然「空括號 method」與「無括號 method」,似乎不太一樣,但相同的地方仍然很多,所以 Scala 把他們視為一體,所以可以互相 override。

範例:「空括號 method」override「無括號 method」
class S1 {
  def m1 = 10 //m1 是 空括號 method
}
class S2 extends S1 { // 繼承 S1  
  override def m1() = 20 // S2.m1() override S1.m
}

範例:「無括號 method」override「空括號 method」
class S1 {
  def m1() = 10 //m1 是 空括號 method
}
class S2 extends S1 { // 繼承 S1  
  override def m1 = 20 // S2.m1 override S1.m()
}

在 Scala 中的 val field,與其說是一個 data field,更像是一個常數的 method,把它當成 method,有時反而更加適當。def 也可以像 method 一樣 override,甚至與 def 之間可以 override。

四. val 與 val 間就像 def 一樣,override 的規定要求相同
範例:val override val
class S1 {
  val m1 = 10 //m1 是 val
}
class S2 extends S1 { // 繼承 S1  
  override val m1 = 20 // val override val
}
範例:同名的 val
class S1 {
  val m1 = 10 //m1 是 val
}
class S2 extends S1 { // 繼承 S1  
  val m1 = 20 // 與 parent 同名的 val,需要宣告override
}

五. val 可以 override def
def 定義一個 method,val 定義一個常數。Scala 中強調不可變資料的重要性,所以 val 可以 override def,但反向是不允許的。
範例:val override def
class S1 {
  def m1() = 10 //m1 是 空括號 method
}
class S2 extends S1 { // 繼承 S1  
  override val m1 = 20 // val override def
}
class Test {
  def test {
    val s2 = new S2
    s2.m1 // 此時會得到 20
    s2.m1() // 糟糕,這裡會出錯,因為 S2 並沒有 m1()
    val s1: S1 = s2
    s1.m1() //可以使用了
  }
}
注意:上例第 11 行的 s2.m1() 是錯誤的,因為 S2 沒有宣告 m1(),只有 val m1,此 val m1 是 override S1.m1()。
但這很奇怪啊?S2 的 val m1 可以 override S1.m1(),但卻不能使用該 m1() method?
這是因為我們需要回歸到「uniform access principle」(下節會提到)的本意,是讓沒有括號的 method 與 val 混用,但此時 s2.m1() 是希望直接使用 method,這就不符合「uniform access principle」原先的想法,所以被禁止了。
你若是還是希望使用 m1() method 的方式,此時需要先將 s2 cast 成一個 S1 的 instance,這時就可以使用了,方式就如同上例的第 12, 13 行一樣。

範例:def override val
class S1 {
  val m1 = 10 //m1 是 val
}
class S2 extends S1 { // 繼承 S1  
  override def m1 = 20 // def override val,這是錯誤的
}

上例打算使用 method 來 override val,當然不會允許,原因是 val 被視為常數,所以一定是不能被 override 的。

沒有留言:

張貼留言