程式扎記: [Scala 小學堂] Scala Gossic : 繼續深入 - 進階型態 (初始抽象 val 成員)

標籤

2016年8月4日 星期四

[Scala 小學堂] Scala Gossic : 繼續深入 - 進階型態 (初始抽象 val 成員)

Source From Here 
初始抽象 val 成員 
在 抽象成員 中最後一個例子,如果在父類別(特徵)中是抽象 val 成員,在子類別實作時,如果執行順序與你的應用程式行為有關時,需注意一下執行的順序問題。如果是抽象類別的話,可以藉由主要建構式來確保執行的順序: 
  1. abstract class Circle(r: Double) {  
  2.     val radius = r  
  3.     val area = Math.Pi * radius * radius  
  4. }  
  5.   
  6. def calRadius = 10.0  
  7.   
  8. class RedCircle(r: Double) extends Circle(r)  
  9.   
  10. val c = new RedCircle(calRadius)  
  11. println(c.area)     // 314.1592653589793  
在建構 RedCircle 時,將 calRadius 函式的傳回值給 RedCircle 建構時使用,所以會先執行 calRadius 取得值,再來進行的是父類別的建構式,將 r 指定給 radius,並將圓面積計算結果指定給 area。如果是 匿名類別 的方式實作的話,只要透過主要建構式傳入參數,則執行的流程是相同的: 
  1. abstract class Circle(r: Double) {  
  2.     val radius = r  
  3.     val area = Math.Pi * radius * radius  
  4. }  
  5.   
  6. def calRadius = 10.0  
  7.   
  8. val c = new Circle(calRadius) {  
  9.             // 其它實作  
  10.         }  
  11.   
  12. println(c.area)     // 314.1592653589793  
但是特徵不能定義主要建構式,所以你可能會如下撰寫程式: 
  1. trait Circle {  
  2.     val radius: Double  
  3.     val area = Math.Pi * radius * radius  
  4. }  
  5.   
  6. def calRadius = 10.0  
  7.   
  8. class RedCircle(r: Double) extends Circle {  
  9.     val radius = r  
  10. }  
  11.   
  12. val c = new RedCircle(calRadius)  
  13. println(c.area)    // 314.1592653589793  
使用建構式來確保執行流程是比較好的。但如果因為某個因素,你要使用匿名類別時: 
  1. trait Circle {  
  2.     val radius: Double  
  3.     val area = Math.Pi * radius * radius  
  4. }  
  5.   
  6. def calRadius = 10.0  
  7.   
  8. val c = new Circle {  
  9.             val radius = calRadius  
  10.         }  
  11.   
  12. println(c.area),     // 0.0  
上 例最後顯示的結果是 0.0。你使用 匿名類別 的方式,實作 Circle 特徵並建立實例,建立實例時的執行順序是,先執行 Circle 內容的初始化區塊,此時 radius 被初始為0.0,而 area 計算的結果也就是0.0,接下來執行 calRadius 函式,將結果設定給 radius。所以最後你使用 c.area 取得的結果會是0.0。解決以上執行順序問題的方式之一,是使用 早期物件初始區段Early object initialization section)來預先初始抽象 val 成員,例如: 
  1. trait Circle {  
  2.     val radius: Double  
  3.     val area = Math.Pi * radius * radius  
  4. }  
  5.   
  6. def calRadius = 10.0  
  7.   
  8. val c = new { val radius = calRadius } with Circle {  
  9.             // 其它定義  
  10.         }  
  11.   
  12. println(c.area)    // 314.1592653589793  
早期物件初始區段中只允許定義 val 成員的實作。執行的順序是,先執行早期物件初始區段,此時 radius 被設定為 calRadius 的執行結果,接著再執行 Circle 特徵中的程式碼,所以 area 根據 radius 的結果計算出來,然後才是匿名類別的建構式區塊。

沒有留言:

張貼留言

網誌存檔

關於我自己

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