程式扎記: [Scala 小學堂] Scala Gossic : 了解更多 - 使用繼承 (遮蔽 與 重新定義)

標籤

2016年6月24日 星期五

[Scala 小學堂] Scala Gossic : 了解更多 - 使用繼承 (遮蔽 與 重新定義)

轉載自 這裡 
前言 : 
Scala 是一個可直譯、編譯、靜態、運行於 JVM 之上、可與 Java 互操作、融合物件導向編程特性與函式編程風格的程式語言. Scala 的繼承作了一些限制,這使你在使用繼承前必須多一份思考. 

遮蔽(Shadow)與重新定義(Override) : 
所謂遮蔽(Shadow),是指在某變數可視範圍內,定義了同名變數,在後者的可視範圍中,取用同一名稱時所用的是後者的定義。例如 : 
  1. val x = 10  
  2. {  
  3.     val x = 20  
  4.     println(x)   // 顯示 20  
  5. }  
  6. println(x)       // 顯示 10  
Scala 支援區塊可視範圍,在上例中,{} 區塊外宣告了一個 x 變數,而區塊內也宣告了一個 x 變數,在區塊中 x 的變數定義遮蔽了區塊外的 x 定義. 如果你熟悉 Java,你會知道這樣的情況在 Java中是允許的 : 
  1. class A {  
  2.     protected int x = 10;  
  3. }  
  4.   
  5. class B extends A {  
  6.     public int x = 20;   // 這邊 x 遮蔽了 A 類別中的 x  
  7. }  
  8.   
  9. public class Main {  
  10.     public static void main(String[] args) {  
  11.         B b = new B();  
  12.         A a = b;  
  13.         System.out.println(b.x);    // 顯示 20  
  14.         System.out.println(a.x);    // 顯示 10, 因為是遮蔽, 不是改寫. 所以類別 A 定義的值仍然有效  
  15.     }  
  16. }  
不過有的 Java 開發人員會誤以為在 B 中重新定義了 x 為公開(事實上是遮蔽),而對於最後的顯示結果感到錯愕。在 Scala 中,繼承時幾乎是不允許遮蔽的,例如,以下的範例會編譯失敗 (value x needs `override' modifier): 
  1. class Parent {  
  2.     protected val x = 10  
  3. }  
  4.   
  5. class Child extends Parent {  
  6.     val x = 20  
  7. }  
這樣的情況下,編譯器會認為,你試圖定義一個父類別中已有的成員(也許你不知道這個事實),如果你要這麼作,編譯器會要求你使用 override 關鍵字,表明你是要重新定義該變數. 以下的程式才可以通過編譯,在 Child 中,x 被重新定義為公開而值設定為 20 : 
  1. class Parent {  
  2.     protected val x = 10  
  3. }  
  4.   
  5. class Child extends Parent {  
  6.     override val x = 20  
  7. }  
  8.   
  9. val c = new Child  
  10. println(c.x)        // 顯示為 20  
在不可視範圍中,沒有遮蔽問題,就不需要使用 override 關鍵字,例如 : 
  1. class Parent {  
  2.     private val x = 10  
  3. }  
  4.   
  5. class Child extends Parent {  
  6.     val x = 20     // 因為 Parent 的 x 在這邊不可視,所以不用 override  
  7. }  
  8.   
  9. val c = new Child  
  10. println(c.x)  
父類別中的成員,在子類別中重新定義時,權限不能縮減,只能重新定義為更寬的權限。例如以下通不過編譯 : 
  1. class Parent {  
  2.     val x = 10  
  3. }  
  4.   
  5. class Child extends Parent {  
  6.     override protected val x = 20  // error, value x has weaker access privileges  
  7. }  
不過這有個例外,就是 private[this],例如以下可以通過編譯 : 
- ShadowVSOverride.scala 代碼 : 
  1. class Parent {  
  2.     var x = 10  
  3. }  
  4.   
  5. class Child extends Parent {  
  6.     private[this] var x = 20  
  7.     def getX = x  
  8.     def setX(v:Int) = { x = v}  
  9. }  
  10.   
  11. var c = new Child  
  12. println(c.x)         // 顯示 10  
  13. println(c.getX)    // 顯示 20  
  14. c.setX(30)            
  15. println(c.x)         // 顯示 10  
  16. println(c.getX)    // 顯示 30  

重 新定義成員時,主要是為了避免影響已使用你程式的客戶端。然而宣告為 private[this] 的成員,是完全只能在類別中使用,不可能透過參考名稱來存 取該成員,也就是客戶端完全不可能使用到你宣告為 private[this] 的成員,所以這種情況是允許的,而在這種情況下,例如上例中,Child 中的 x 在類別中,遮蔽了 Parent 的 x,因此透過 getX 取回時會是 20 的值. 另外要注意的是,你不可以重新定義 var 為 val,例如下例通不過編譯 
  1. class Parent {  
  2.     var x = 10  
  3. }  
  4.   
  5. class Child extends Parent {  
  6.     override val x = 10    // error, value x cannot override a mutable variable  
  7. }  
這可以理解,如果 x 在父類別中是可變動的,若父類別中某些演算方法會改變 x 的值,在繼承後你將之設為 val,原本那些演算方法該怎麼辦呢. 

沒有留言:

張貼留言

網誌存檔