在 Java 這些共通的部份,也可能以 class singleton 的方式來表達。class singleton 的意思是該 class 只會有一個 instance,大家共通的 behavior 會以呼叫這個 single instance 的 method 來達成。
製作 singleton 程式時,通常在程式中需要宣告一個 static data field,來保存該 class 這個唯一的 instance。使用 static data field 來放置 singleton object,已幾乎成為共識。
通常我們不會在程式啟動時,就將 「singleton object」 create 出來,會延遲到程式需要該 instance 時才 create 該 instance。這導致另一個麻煩,不可重複 create instance,否則,singleton 的唯一性會被破壞,可能造成其他問題。
我們常用 singleton 來替代「共通需求」,在 Scala,更將這個概念發揮到極致。
在 Scala 每個東西都是一個 object,沒有所謂大家共通的概念,所以共通行為是不可行的。Scala 已將 static 移除,以 singleton 取而代之。S
由於 singleton 的需求眾多,為避免 programmer 沒掌握住 singleton 的 creation,因此 Scala 直接代勞。我們只需宣告 singleton object,Scala 負責幫我們處理該 object 的 creation。
在 Scala 中使用 object 這個 keyword 來宣告 singleton object,讓我們看例子
object S { def print(name: String) = println(name) }上例宣告一個 object S,請把這樣的宣告看成「在我們的系統中,有一個 object,名叫 S」。這個 object 與「使用變數宣告,然後 create instance 的 object」二者一樣。
讓我們用例子說明
object S { def print(name: String) = println(name)} var x = "ABC"上例,我們可以看成「程式有一個 object S,也有一個 object x」,S 與 x 沒有位階上的差異。程式可以直接呼叫 x 做工作,也可呼叫 S 做工作。
object S { def print(name: String) = println(name) } class UseS { def m1(name: String) = S.print(name) // 使用 S 這個 object }上例的 UseS 的 m1 method 呼叫 S 這個 object 做事。
技術性來講,object keyword 所做的事是定義一個 class ,並且產生與該 class 同名的一個 instance。所以 object keyword 所定義的東西與使用 class keyword 所定義出來的東西一模一樣,我們不需因為 object 這個 keyword 而有不必要的害怕或想像。
在 Java ,一個 class 可能包含 static 與 non-static 的部份。
若你將該 Java class 改寫成 Scala,原來 static 的部份,我們應該把它放入 object 的宣告,non-static 的部份我們應該把它放入 class 的宣告中。
原來的 Java class
class C { static void m_static(){System.out.println("static_m");} void m_instance(){System.out.println("instance_m");} }改寫成的 Scala class
object C { def m_static = println("static_m") } class C { def m_instance() = println("instance_m") }
同名的 class 與 object 關係非常密切, Scala 把他們互稱為 companion(陪伴)。所以 class C 是 object C 的 companion class,object C 是 class C 的 companion object。
object keyword 所做的事,比只產生該 class 的 instance 還多
我們舉以下的例子說明
object S { def print(name: String) = println(name) }1. object keyword 主要會產生一個 companion object 的 classfile,companion object 的名稱,是原名稱後面加上$。比如上例,object S 的宣告會產生 S$ 的 classfile
2. 若程式沒宣告同名的 class(即沒有 companion class),object keyword 自動會產生 companion class 的 classfile。
companion class 具有 companion object 中的所有 method,但會變成 static method,這些 method 會 forward 給 companion object 對應的 method。
如上例,object S 會產生 S 的 classfile,且 S classfile 中有 static print 這個 method。這個 method 會 forward 給 S$ 的 print。
產生 companion class 對應的 static method,主要原因是讓 Java 程式可以使用 companion class 的 static method 呼叫到 Scala 中 singleton object 的 method,這樣一來 singleton object 扮演 static 角色的感覺就更加強烈。
範例:只有 object,沒有 companion class時
範例:有 object,也有 companion class時
class S2 產生 S2.class。
object S2 產生 S2$.class,且將一些 method 放入 S2.class。
我們仔細觀看 S2.class,S2有 m1 method,這是 S2 本身定義的。S2也有 sayHello,這是 object S2定義的 method。
在本例中 S2.class 的 m1 method,呼叫 object S2 的 syaHello method,我們來看如何呼叫
在 m1 method 中,可以很清楚看到呼叫 S2$.sayHello,印證 object S2 被編譯成 S2$。
對於 singleton 我們還有一點需要特別加以說明,在 OO 裡面,static method 是不能被 override 的,這個你應該要認識,因為 override 是 instance 才會有的動作,在 static level 做 override 是沒有意義的。有些語言,甚至將 static method 編譯為 final,表示不能再被 override。
Java 的 static method 也是同樣的情況,不能被 override 的。
若你在 subclass 宣告一個與 parent class 相同名稱的 static method,Java 允許你這樣做,可是其實是危險的。因為 Java compiler 其實認定你在 subclass 重新宣告了一個新的 method,只是名稱與 parent class 的相同,所以效果並不是 override。下例中,我們使用 @Override 來驗證這個推論。
範例:
class J { static void m1() { System.out.println("J.m1"); } static void m2() { System.out.println("J.m2"); } } class J11 extends J1 { static void m1() { // 注意本 method 並非 override J.m1,真正的效果與定義一個新的 method 完全相同 System.out.println("J11.m1"); } @Override //我們使用 @Override 來宣告要 override J.m2,這裡會產生 compile error。 static void m2() { System.out.println("J11.m2"); } }Singleton object 主要是處理原來 Java 所需要的 static 的部份,以剛剛的論述我們知道 static 的部份不應該加以 override,或者說 static 的部份做繼承是沒有意義的動作。基於這個理由,Scala 的 singleton object,也是不能繼承的,這件事你應該樣記住。
object S class S1 extends S//class 繼承 object,錯誤!!
object S object S1 extends S//object 繼承 object,錯誤!!注意:但 object 可以繼承 class。這是因為 object 取的是 parent class 的 instance 部份,當然允許繼承
class S object S1 extends S//object 繼承 class,這是很正確的作法!!
沒有留言:
張貼留言