程式扎記: [Scala 小學堂] Scala Gossic : 了解更多 - 使用繼承 (無參數方法)

標籤

2016年6月28日 星期二

[Scala 小學堂] Scala Gossic : 了解更多 - 使用繼承 (無參數方法)

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

無參數方法 : 
你定義了一個 Ball 類別 : 
  1. class Ball(r: Double) {  
  2.     def radius = r  
  3. }  
類別中,radius 方法不接受任何參數。在Scala中,不接受任何參數的方法,可以省略()的撰寫,這稱之為無參數方法Parameterless method),對於僅讀取而不改變物件狀態、且不接受任何參數的方法,在 Scala 中的慣例會省略括號,表示該方法不會有任何邊際效應Side effect),相對地,如果不接受任何參數的方法會產生邊際效應,則保留括號的撰寫,稱之為空括號方法(Empty-paren method), 今天假設有個客戶端使用了你的 Ball 類別 : 
  1. val ball = new Ball(10.0)  
  2. println(ball.radius)// 顯示 10.0  
同樣地,如果操作方法時,該方法如果不會產生邊際效應,則呼叫方法時慣例上也是省略括號。如果哪天你改變了 Ball 類別之撰寫 : 
  1. class Ball(val radius: Double)  
就剛才那個客戶端的程式碼並不會受到影響,這是 固定存取原則Uniform access principle) 的簡單例子。固定存取原則指的是,提供客戶端服務時所使用的名稱必須固定,無論該服務是透過計算或既有的結果. 就如同上例,客戶端要取得球的半徑,都是透過radius這個名稱來取得,無論是你將 radius 實作為方法或者是變數. 要注意的是在 Scala中,只有值(Value)與型態(Type)兩個名稱空間(Namespace),其中 : 
* 資料成員(Field)、方法(Method)、套件(Package)與單例(Singleton)物件屬於 (Value)名稱空間.
* 類別(Class)與特徵(Trait)屬於 型態(Type)名稱空間.
Ps. 由於單例物件與類別分屬於值與型態名稱空間,所以才可以相同的名稱形成伴侶.

由於方法與資料成員屬於同一個名稱空間,所以同一個類別中,資料成員不可以與方法是相同的名稱. 例如 : 
  1. class Ball {  // 這個類別會編譯錯誤  
  2.     private val radius = 10  
  3.     def radius = 20  
  4. }  
即使是繼承,如果子類別中有與父類別中的成員相同名稱的成員也不行。如果子類別中的方法名稱與父類別的方法名稱相同,則是重新定義方法,你必須使用 override 關鍵字指定。例如 : 
  1. class Ball(r: Double) {  
  2.     def volume = 4 * Math.Pi * Math.pow(r, 3) / 3  
  3.     override def toString = "radius: " + r +  
  4.                             "\nvolume: " + volume  
  5. }  
  6.   
  7. class IronBall(r: Double) extends Ball(r)  
上例中定義了一個 IronBall,如果有客戶使用這個 IronBall 類別 : 
  1. val ball = new IronBall(10.0)  
  2. println(ball.volume)  
今天基於某個理由,你改寫了 IronBall : 
  1. class IronBall(r: Double) extends Ball(r) {  
  2.     override val volume = 4 * Math.Pi * Math.pow(r, 3) / 3  
  3. }  
在 IronBall 中使用 val 定義了 volume 這個名稱,由於方法與資料成員屬於同一個名稱空間,如果你確定在子類別中要這麼作,必須重新定義,也就是使用 override 關鍵字指定,在 Scala 中,允許你將無參數方法重新定義為 val 資料成員。對於先前使用 IronBall 的客戶端而言,並沒有差別,這是固定存取原則的另一個簡單例子. 注意!你可以將父類別的無參數方法在子類別中重新定義為 val 資料成員,但不能將父類別的 val 資料成員在子類別中重新定義為無參數方法. 例如以下會編譯錯誤 : 
  1. class Ball(r: Double) {  
  2.     val volume = 4 * Math.Pi * Math.pow(r, 3) / 3  
  3.     override def toString = "radius: " + r +  
  4.                             "\nvolume: " + volume  
  5. }  
  6.   
  7. class IronBall(r: Double) extends Ball(r) {  
  8.     // error, method volume is not stable  
  9.     override def volume = 4 * Math.Pi * Math.pow(r, 3) / 3  
  10. }  
不允許的理由在於,原先 volume 是個 val,也就是值固定不變,若能重新定義為方法,則方法的傳回值可能會因計算而不同,如果這個修改允許,你的客戶端在 IronBall 修改之後,執行的結果就會受到影響(如果 IronBall 的狀態是可變的話).

沒有留言:

張貼留言

網誌存檔