Kotlin 知識梳理系列文章
Kotlin 知識梳理(1) - Kotlin 基礎(chǔ)
Kotlin 知識梳理(2) - 函數(shù)的定義與調(diào)用
Kotlin 知識梳理(3) - 類、對象和接口
Kotlin 知識梳理(4) - 數(shù)據(jù)類鹤竭、類委托 及 object 關(guān)鍵字
Kotlin 知識梳理(5) - lambda 表達式和成員引用
Kotlin 知識梳理(6) - Kotlin 的可空性
Kotlin 知識梳理(7) - Kotlin 的類型系統(tǒng)
Kotlin 知識梳理(8) - 運算符重載及其他約定
Kotlin 知識梳理(9) - 委托屬性
Kotlin 知識梳理(10) - 高階函數(shù):Lambda 作為形參或返回值
Kotlin 知識梳理(11) - 內(nèi)聯(lián)函數(shù)
Kotlin 知識梳理(12) - 泛型類型參數(shù)
一迫肖、本文概要
本文是對<<Kotlin in Action>>
的學(xué)習(xí)筆記,如果需要運行相應(yīng)的代碼可以訪問在線環(huán)境 try.kotlinlang.org闲坎,這部分的思維導(dǎo)圖為:
二疫粥、數(shù)據(jù)類和類委托
2.1 數(shù)據(jù)類:自動生成通用方法的默認實現(xiàn)
在平時的開發(fā)中茬斧,我們往往會使用許多的xxBean
對象用作數(shù)據(jù)容器,而在定義這些對象時梗逮,一般會重寫它的以下三個方法:
-
equals
:用來比較實例 -
hashCode
:用來作為例如HashMap
這種基于哈希容器的類 -
toString
:用來為類生成按聲明順序排列的所有字段的字符串表達形式
在Kotlin
中项秉,只需要為你的類添加data
關(guān)鍵字,以上這些必要的方法就會自動生成好慷彤,例如下面的例子娄蔼,我們演示了以上三個方法的作用:
運行結(jié)果為:
equals
和hashCode
方法會將所有在主構(gòu)造方法中聲明的屬性納入考慮:
-
equals
方法會檢測所有的屬性的值是否相等 -
hashCode
方法會返回一個根據(jù)所有屬性生成的哈希值
數(shù)據(jù)類和不可變性
在設(shè)計數(shù)據(jù)類時,應(yīng)當(dāng)盡量只使用只讀的屬性底哗,讓數(shù)據(jù)類的實例不可變岁诉,因為如果不這樣,被用作鍵的對象在加入HashMap
或者類似容器后被修改了跋选,容器會進入一種無效的狀態(tài)涕癣。
為了讓使用不可變對象變得容易,Kotlin
編譯器為它們生成了copy
方法前标,并在copy
的同時修改某些屬性的值坠韩,copy
出來的副本有著單獨的聲明周期而且不會影響代碼中引用原始實例的位置,使用方法如下:
2.2 類委托
當(dāng)我們需要向一個類添加一些行為時炼列,一般有兩種做法:
-
繼承這個類只搁,在子類中增加方法
這種方法的缺點是:當(dāng)系統(tǒng)不斷演進并且基類的實現(xiàn)被修改或者新方法被添加進去時,你做出的關(guān)于類的行為的假設(shè)會失效唯鸭。 -
使用裝飾器模式
本質(zhì)是創(chuàng)建一個新類须蜗,實現(xiàn)與原始類一樣的接口并將原來的類的實例作為一個字段保存硅确。與原始類擁有同樣行為的方法不用修改目溉,只需要直接轉(zhuǎn)發(fā)到原始類的實例。這種方法的缺點是:需要相當(dāng)多的樣板代碼菱农。
而Kotlin
將委托作為一個語言級別的功能做了頭等支持缭付。無論什么時候?qū)崿F(xiàn)一個接口辰斋,你都可以使用by
關(guān)鍵字 將接口的實現(xiàn)委托到另一個對象滤否;當(dāng)需要修改某些方法的行為時,可以重寫它們瑰步,這樣你的方法就會被調(diào)用而不是使用生成的方法的妖,可以保留感到滿意的委托給內(nèi)部的實例中的默認實現(xiàn)绣檬。
運行結(jié)果為:
三、object 關(guān)鍵字
object
關(guān)鍵字在多種情況下出現(xiàn)嫂粟,它的核心理念為:這個關(guān)鍵字 定義一個類并同時創(chuàng)建一個實例娇未,下面我們介紹它的三個應(yīng)用場景:
- 對象聲明 是定義單例的一種方式
- 伴生對象 可以持有工廠方法和其它與這個類相關(guān),但在調(diào)用時并不依賴類實例的方法星虹,它們的成員可以通過類名來訪問零抬。
-
對象表達式 用來替代
Java
的匿名內(nèi)部類镊讼。
3.1 對象聲明:創(chuàng)建單例易如反掌
在Java
中,單例模式通常是使用private
構(gòu)造方法平夜,并且用靜態(tài)字段來持有這個類僅有的實例蝶棋。
而在Kotlin
中,通過使用對象聲明功能忽妒,將類聲明與該類的單一實例聲明結(jié)合到了一起玩裙。
- 對象聲明通過
object
關(guān)鍵字引入,一個對象聲明可以非常高效地以一句話來定義一個類和一個該類的變量段直。 - 一個對象聲明可以包含屬性献酗、方法、初始化語句塊等的聲明坷牛,但是 不允許聲明構(gòu)造方法罕偎,這是因為對象在定義的時候就已經(jīng)創(chuàng)建了,不需要在其他地方調(diào)用構(gòu)造方法京闰。
- 對象聲明允許使用對象名加
.
字符的方式來調(diào)用方法和訪問屬性颜及。
繼承自接口的對象聲明
對象聲明可以繼承自類和接口,這通常在你使用的框架需要去實現(xiàn)一個接口蹂楣,但是你的實現(xiàn)不包含任何狀態(tài)的時候很有用俏站。
在類中聲明對象
在類中使用對象聲明時,這樣的對象同樣只有一個單一實例:它們在每個容器類的實例中具有相同的實例痊土。
運行結(jié)果為:
在 Java 中使用 Kotlin 對象
如果要在Java
中使用Kotlin
中的聲明對象肄扎,可以通過訪問靜態(tài)的INSTANCE
字段:
3.2 伴生對象:工廠方法和靜態(tài)成員的地盤
Kotlin
的類不能擁有靜態(tài)成員,作為替代赁酝,Kotlin
依賴包級別函數(shù)(在大多數(shù)情形下能夠替代Java
的靜態(tài)方法)和對象聲明(在其他情況下替代Java
的靜態(tài)方法犯祠,同時還包括靜態(tài)字段),在大多數(shù)情況下酌呆,推薦使用頂層函數(shù)衡载,但是頂層函數(shù)不能訪問類的private
變量。
因此隙袁,如果你需要寫一個 在沒有類實例的情況下 調(diào)用但是需要 訪問類內(nèi)部的函數(shù)痰娱,可以將其寫成那個類中的 對象聲明的成員。
在類中定義的對象之一可以使用一個特殊的關(guān)鍵字來標(biāo)記 companion菩收,如果這樣做梨睁,就獲得了直接 通過容器類名稱來訪問這個對象的方法和屬性的能力,不再需要顯示地指明對象的名稱娜饵,下面是一個基礎(chǔ)的示例:
運行的結(jié)果為:
使用工廠方法創(chuàng)建對象
下面是使用伴生對象來實現(xiàn)工廠方法的例子:
3.3 作為普通對象使用的伴生對象
伴生對象是一個聲明在類中的普通對象坡贺,它可以有名字,實現(xiàn)一個接口或者有擴展函數(shù)或?qū)傩浴<僭O(shè)我們需要在對象和JSON
之間進行序列化和反序列化拴念,可以將序列化的邏輯放在伴生對象中钧萍。
運行結(jié)果為:
在大多數(shù)情況下,通過包含伴生對象的類的名字(也就是例子中的
Person
類)來引用伴生對象政鼠,所以不必關(guān)心它的名字风瘦,如果省略了伴生對象的名字,默認的名字將會分配為Companion
公般。
在伴生對象中實現(xiàn)接口
就像其它對象聲明一樣万搔,伴生對象也可以實現(xiàn)接口,可以將包含它的類的名字當(dāng)做實現(xiàn)了該接口的對象實例來使用官帘。
運行結(jié)果為:
伴生對象擴展
在 Kotlin 知識梳理(2) - 函數(shù)的定義與調(diào)用 中瞬雹,我們介紹了通過擴展函數(shù),可以通過代碼庫中其它地方定義類實例調(diào)用的方法刽虹,但是如果你需要定義可以通過 類自身調(diào)用的方法酗捌,就像伴生對象方法或者Java
靜態(tài)方法該怎么辦呢?
舉例來說涌哲,如果類有一個伴生對象胖缤,可以通過在其上定義擴展函數(shù)來做到這一點,即類C
有一個伴生對象阀圾,并且在C.Companion
上定義了一個擴展函數(shù)func
哪廓,則可以通過C.fun()
來調(diào)用它。
下面初烘,我們?yōu)?code>Person類的伴生對象定義一個擴展函數(shù):
當(dāng)調(diào)用toJson
就像是它是一個伴生對象定義的方法一樣涡真,但是實際上它是作為擴展函數(shù)在外部定義的。而為了能夠為你的類定義擴展肾筐,必須在其中聲明一個對象哆料,即使是空的。
3.4 對象表達式:改變寫法的匿名內(nèi)部類
object
關(guān)鍵字不僅能夠用來表明單例式的對象局齿,還能用來聲明 匿名對象剧劝,它替代了Java
中匿名內(nèi)部類的用法橄登。例如抓歼,讓我們來看看怎樣將一個典型的匿名內(nèi)部類用法轉(zhuǎn)換成Kotlin
:
運行結(jié)果為:
將匿名對象存儲到變量中
除了去掉對象的名字外,語法與對象聲明相同的拢锹。對象表達式聲明了一個類并創(chuàng)建了該類的一個實例谣妻,但是沒有給這個類或是實例分配一個名字。通常來說卒稳,它們都是不需要名字的蹋半,因為你會將這個對象用作一個函數(shù)調(diào)用的參數(shù)。如果你需要給對象分配一個名字充坑,可以將其存儲到一個變量中:
在對象表達式中修改變量的值
與Java
的匿名類一樣减江,在對象表達式中的代碼可以訪問創(chuàng)建它的函數(shù)中的變量染突,但是與Java
不同,訪問并被限制在final
變量辈灼,還可以在對象表達式中修改變量的值份企。
運行結(jié)果為:
更多文章,歡迎訪問我的 Android 知識梳理系列:
- Android 知識梳理目錄:http://www.reibang.com/p/fd82d18994ce
- 個人主頁:http://lizejun.cn
- 個人知識總結(jié)目錄:http://lizejun.cn/categories/