書名: Scala編程(第5版)作者名: (德)馬丁·奧德斯基等本章字?jǐn)?shù): 1179字更新時(shí)間: 2022-05-06 15:51:36
4.3 單例對(duì)象
正如第1章提到的,Scala比Java更面向?qū)ο蟮囊稽c(diǎn)是,Scala的類不允許有靜態(tài)(static)成員。對(duì)于此類使用場(chǎng)景,Scala提供了單例對(duì)象。單例對(duì)象的定義看上去與類定義很像,只不過class關(guān)鍵字被替換成了object關(guān)鍵字。參考示例4.2。

示例4.2 ChecksumAccumulator類的伴生對(duì)象
在示例4.2中的單例對(duì)象名為ChecksumAccumulator,與前一個(gè)示例中的類名一樣。當(dāng)單例對(duì)象與某個(gè)類共用同一個(gè)名稱時(shí),它被稱作這個(gè)類的伴生對(duì)象(companion object)。必須在同一個(gè)源碼文件中定義類和類的伴生對(duì)象。同時(shí),類又叫作這個(gè)單例對(duì)象的伴生類(companion class)。類和它的伴生對(duì)象可以互相訪問對(duì)方的私有成員。
ChecksumAccumulator單例對(duì)象有一個(gè)名稱為calculate的方法,用于接收一個(gè)String,并計(jì)算這個(gè)String的所有字符的校驗(yàn)和(checksum)。它同樣也有一個(gè)私有的字段,即cache,這是一個(gè)緩存了之前已計(jì)算過的校驗(yàn)和的可變映射。[3]方法的第一行,即“if (cache.contains(s))”,用于檢查緩存以確認(rèn)傳入的字符串是否已經(jīng)被包含在映射中。如果是,就返回映射的值,即cache(s)。如果沒有,則執(zhí)行else子句,計(jì)算校驗(yàn)和。else子句的第一行定義了一個(gè)名稱為acc的val,用一個(gè)新的ChecksumAccumulator實(shí)例初始化。[4]接下來的一行是一個(gè)for表達(dá)式,遍歷傳入字符串的每一個(gè)字符,通過調(diào)用toByte方法將字符轉(zhuǎn)換成Byte,然后將Byte傳遞給acc指向的ChecksumAccumulator實(shí)例的add方法。[5]在for表達(dá)式執(zhí)行完成以后,方法的下一行調(diào)用acc的checksum方法,從傳入的String中得到其校驗(yàn)和,保存到名稱為cs的val。再往下一行,即cache += (s -> cs),將傳入的字符串作為鍵,計(jì)算出的整型的校驗(yàn)和作為值,這組鍵/值對(duì)被添加到緩存映射中。該方法的最后一個(gè)表達(dá)式,即cs,確保了該方法的結(jié)果是這個(gè)校驗(yàn)和。
如果你是Java程序員,則可以把單例對(duì)象當(dāng)作用于安置那些用Java時(shí)打算編寫的靜態(tài)方法。可以用類似的方式訪問單例對(duì)象的方法:?jiǎn)卫龑?duì)象名、英文句點(diǎn)和方法名。例如,可以像這樣調(diào)用ChecksumAccumulator單例對(duì)象的calculate方法:

不過,單例對(duì)象并不僅僅用來存放靜態(tài)方法。它是一等(first-class)的對(duì)象。可以把單例對(duì)象的名稱想象成附加在對(duì)象身上的“名稱標(biāo)簽”:

定義單例對(duì)象并不會(huì)定義類型(在Scala的抽象層級(jí)上是這樣的)。當(dāng)只有ChecksumAccumulator的對(duì)象定義時(shí),并不能定義一個(gè)類型為ChecksumAccumulator的變量。確切地說,名稱為ChecksumAccumulator的類型是由這個(gè)單例對(duì)象的伴生類來定義的。不過,單例對(duì)象可以擴(kuò)展自某個(gè)超類,還可以混入特質(zhì)。你可以通過這些類型來調(diào)用它的方法,用這些類型的變量來引用它,還可以將它傳入那些預(yù)期為這些類型的入?yún)⒌姆椒ㄖ小N覀儗⒃诘?2章給出單例對(duì)象繼承類和特質(zhì)的示例。
類和單例對(duì)象的一個(gè)區(qū)別是單例對(duì)象不接收參數(shù),而類可以。由于無法用new實(shí)例化單例對(duì)象,也就沒有任何手段來向它傳參。每個(gè)單例對(duì)象都是通過一個(gè)靜態(tài)變量引用合成類(synthetic class)的實(shí)例來實(shí)現(xiàn)的,因此單例對(duì)象在初始化的語義上與Java的靜態(tài)成員是一致的。[6]尤其體現(xiàn)在,單例對(duì)象在有代碼首次訪問時(shí)才會(huì)被初始化。
不與某個(gè)伴生類共用同一個(gè)名稱的單例對(duì)象叫作獨(dú)立對(duì)象(standalone object)。獨(dú)立對(duì)象有很多用途,包括收集相關(guān)的工具方法,或者定義Scala應(yīng)用程序的入口,等等。下一節(jié)將介紹這樣的用法。
- WebAssembly實(shí)戰(zhàn)
- 控糖控脂健康餐
- C/C++算法從菜鳥到達(dá)人
- Network Automation Cookbook
- 新手學(xué)Visual C# 2008程序設(shè)計(jì)
- Full-Stack React Projects
- 從Excel到Python:用Python輕松處理Excel數(shù)據(jù)(第2版)
- Mastering Linux Security and Hardening
- 開源項(xiàng)目成功之道
- Raspberry Pi Robotic Projects(Third Edition)
- Java程序設(shè)計(jì)實(shí)用教程(第2版)
- Getting Started with JUCE
- Unreal Engine 4 Game Development Essentials
- Visual FoxPro程序設(shè)計(jì)
- Access 2013數(shù)據(jù)庫應(yīng)用案例課堂