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

標籤

2016年7月28日 星期四

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

Source From Here 
抽象成員 
在這邊所謂的 抽象成員,指的是在抽象類別或特徵(Trait)所定義的成員,像是使用 valvar 宣告的資料成員(Field),或是使用 def 所宣告的方法。在抽象類別或特徵(Trait)中定義抽象資料成員,不需要提供初始值,定義抽象方法時,不需要提供實作,繼承的子類別必須提供資料成員初始值或者是方法實作。例如: 
  1. trait Something {  
  2.     val some: String  
  3.     def doSome: String  
  4. }  
  5.   
  6. class Other extends Something {  
  7.     val some = "some"  
  8.     def doSome = "do...some"  
  9. }  
如果是個 無參數方法,在子類別可以將之重新定義為 val 成員,這在 無參數方法 中曾經介紹過,不過對於抽象方法而言,子類別重新定義為 val 時,不需加上 override 修飾。例如: 
  1. trait Something {  
  2.     val some: String  
  3.     def data: String  
  4. }  
  5.   
  6. class Other extends Something {  
  7.     val some = "some"  
  8.     val data = "data"  
  9. }  
如果是個 var 抽象成員,在子類別中可以這麼定義其初始值: 
  1. trait Something {  
  2.     var some: String  
  3. }  
  4.   
  5. class Other extends Something {  
  6.     var some = "some"  
  7. }  
在 屬性存取方法 中介紹過,var 成員其實可以實作為特殊名稱的存取方法。所以事實上,你定義一個 var 抽象成員時,相當於定義一組抽象方法,例如上例中的 Something,相當於以下: 
  1. trait Something {  
  2.     def some: String  
  3.     def some_=(s: String)  
  4. }  
所以,對於一個抽象 var 成員,你可以如下實作沒有問題,編譯器並不會抱怨: 
  1. trait Something {  
  2.     var some: String  
  3. }  
  4.   
  5. class Other extends Something {  
  6.     private[this] var s = "some"  
  7.     def some: String = "XD"  
  8.     def some_=(s: String) { this.s = s }  
  9. }  
如果你想實作某個抽象類別,但不想要定義出類別名稱,也就是你想要以 匿名類別Anonymous class)的方式對抽象類別實作並實例化,則以下是個例子: 
  1. abstract class Something(val s: String) {  
  2.     def doSome: String  
  3. }  
  4.   
  5. val something = new Something("XD") { def doSome =  s + "...done" }  
  6.   
  7. println(something.doSome)  // XD...done  
以下則是個使用特徵的例子: 
  1. trait Something {  
  2.     def doSome: String  
  3. }  
  4.   
  5. val something = new Something { def doSome =  "XD...done" }  
  6.   
  7. println(something.doSome)  
抽象類別可以宣告主要建構式,但特徵不行,這決定了你是否透過主要建構式傳入參數來初始化抽象成員。就前兩個例子而言,結果沒什麼不同,但如果是以下的例子,需注意一下執行的順序: 
  1. abstract class Something(val s: String) {  
  2.     println("主要建構式")  
  3.     def doSome: String  
  4. }  
  5.   
  6. def doIt = {  
  7.     println("函式執行")  
  8.     "XD"  
  9. }  
  10.   
  11. val something = new Something(doIt) { def doSome =  s + "...done" }  
  12. println(something.doSome)  
在使用 匿名類別 實作 Something 並建立實例時,要傳入建構式參數,值是由 doIt 函式決定,所以 doIt 函式會先執行,接著是 Something 主要建構式,也就是會先顯示"函式執行",接著再顯示 "主要建構式",最後呼叫 something.doSome 時,顯示 "XD...done"。 

如果是以下的例子: 
  1. trait Something {  
  2.     println("主要建構式")  
  3.     def doSome: String  
  4. }  
  5.   
  6. def doIt = {  
  7.     println("函式執行")  
  8.     "XD"  
  9. }  
  10.   
  11. val something = new Something { def doSome =  doIt + "...done" }  
  12. println(something.doSome)  
這邊一樣以匿名類別實作 Something 並建構實例,此時會先執行 Something 主要建構式,也就是先顯示 "主要建構式",但在執行 something.doSome 時,才會執行 doIt 函式取得其結果,也就是顯示 "函式執行",接著與 "...done" 字串串接後傳回,也就是最後顯示 "XD...done"。或許你會覺得,放在 doSome 方法中的 doIt 很顯然地,必須呼叫 doSome 方法才會被執行不是嘛?是的!但如果是以下這個例子,就不是那麼明顯了: 
  1. trait Something {  
  2.     println("主要建構式")  
  3.     def doSome: String  
  4. }  
  5.   
  6. def doIt = {  
  7.     println("函式執行")  
  8.     "XD"  
  9. }  
  10.   
  11. val something = new Something { val doSome =  doIt + "...done" }  
  12. println(something.doSome)  
這邊一樣以 匿名類別 實作 Something 並建構實例,此時會先執行 Something 主要建構式,也就是先顯示 "主要建構式",接著執行匿名類別主要建構式,此時呼叫 doIt 以取得結果,所以會顯示 "函式執行",再來是 something.doSome 呼叫,所以最後是顯示 "XD...done"。

沒有留言:

張貼留言

網誌存檔