程式扎記: [Scala 小學堂] Scala Gossic : 了解更多 - 定義類別 (屬性存取方法)

標籤

2016年6月21日 星期二

[Scala 小學堂] Scala Gossic : 了解更多 - 定義類別 (屬性存取方法)

轉載自 這裡 
前言 : 
Scala 是一個可直譯、編譯、靜態、運行於 JVM 之上、可與 Java 互操作、融合物件導向編程特性與函式編程風格的程式語言. Scala 本身具有擴充性,不過這必須了解更多語法特性與細節. Scala 建構式有些限制,使用方法可以定義所謂運算子(Operator),提供語法蜜糖與存取方法. 

屬性存取方法 : 
如果你如下定義一個類別 : 
  1. class Ball {  
  2.     var radius = 10.0  
  3. }  
  4.   
  5. val ball = new Ball  
  6. ball.radius = 20.0  
  7. println(ball.radius)           // 顯示 20.0  
乍看之下,這個定義似乎不被鼓勵,因為客戶端可以直接存取 radius 成員。如果是 Java 的話,為將 radius 設為private,並設值方法(Setter)、取值方法(Getter),必要時建立保護條件判斷 : 
  1. public class Ball {  
  2.     private double radius = 10.0;  
  3.     public void setRadius(double radius) {  
  4.         if(radius < 0) {  
  5.             throw new IllegalArgumentException("不能是負數");  
  6.         }  
  7.   
  8.         this.radius = radius;  
  9.     }  
  10.     public double getRadius() {  
  11.         return radius;  
  12.     }  
  13. }  
事實上,當你在類別中定義一個非 private 的 var 成員時,其實隱含地都會為其建立一對存(Modifier)取(Accessor)方法,也就是設值方法、取值方法,它們用以存取 private[this] var 的資料成員。例如,先前的 Scala 所定義的 Ball 類別,事實上相當於以下的定義方式 : 
- AttrAccess1.scala 代碼 :
  1. class Ball {  
  2.     private[this] var r = 10.0  
  3.     def radius: Double = r             // Accessor  
  4.     def radius_=(a: Double) { r = a }  // Modifier  
  5. }  
  6.   
  7. val ball = new Ball  
  8. ball.radius = 20.0  
  9. println(ball.radius)       // 顯示 20.0  

private [X] 中的 X 是 Scala 為 protected 及 private 所提供的修飾詞(Qualifier),用以提供更細部的可視範圍(Scope)修飾。 private[this] 表示所宣告的變數,只能在類別定義中被存取,任何實例化物件時再存取該屬性的動作都不允許,例如 : 
  1. class Some {  
  2.     private[this] var data = "XD"  
  3.     def doSome(s: Some) = data + s.data  // 錯誤,不可以使用s.data存取  
  4. }  
上例中由於 data 使用 private[this] 宣告,所以即使是傳入 Some 的 Some 實例,也不可以直接存取其 data(Java 中使用 private 宣告的成員是可以這麼作的). 所以若你要在存取 Ball 物件的 radius 屬性時,提供保護條件,則可以如下 : 
- AttrAccess2.scala 代碼 :
  1. class Ball {  
  2.     private[this] var r = 10.0  
  3.     def radius: Double = r  
  4.     def radius_=(a: Double) {   
  5.         require(a >= 0)  
  6.         r = a   
  7.     }  
  8. }  
  9.   
  10. val ball = new Ball  
  11. ball.radius = 20.0  
  12. println(ball.radius)   // 顯示 20.0  
  13. ball.radius = -1       // 錯誤,IllegalArgumentException: requirement failed  
  14. println(ball.radius)  

屬性存取方法並不一定得有對應的內部資料成員,也可以是計算的結果。例如 : 
- AttrAccess3.scala 代碼 :
  1. class Ball {  
  2.     private[this] var r = 10.0  
  3.     def radius: Double = r  
  4.     def radius_=(a: Double) {   
  5.         require(a >= 0)  
  6.         r = a   
  7.     }  
  8.     def volume: Double = 4.0 / 3.0 * Math.Pi * Math.pow(r, 3)  
  9.     def volume_=(v: Double) {  
  10.         require(v >= 0)  
  11.         r = Math.pow((3.0 * v) / (4.0 * Math.Pi), 1.0 / 3.0)  
  12.     }  
  13. }  
  14.   
  15. val ball = new Ball  
  16. ball.radius = 10.0  
  17. println(ball.volume)    // 顯示 4188.790204786391  
  18. ball.volume = 5000  
  19. println(ball.radius)    // 顯示 10.60784417947055  

上例中,實際上並沒有 volume 對應的內部資料成員,而是將計算對應至 r 或者是從 r 計算出 volume

Supplement 
Tutorialspoint - Scala Access Modifiers 
Scala access modifiers and qualifiers in detail

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!