Kotlin 知識梳理(4) 資料類、類委託 及 object 關鍵字

澤毛發表於2017-12-21

一、本文概要

本文是對<<Kotlin in Action>>的學習筆記,如果需要執行相應的程式碼可以訪問線上環境 try.kotlinlang.org,這部分的思維導圖為:

Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字

二、資料類和類委託

2.1 資料類:自動生成通用方法的預設實現

在平時的開發中,我們往往會使用許多的xxBean物件用作資料容器,而在定義這些物件時,一般會重寫它的以下三個方法:

  • equals:用來比較例項
  • hashCode:用來作為例如HashMap這種基於雜湊容器的類
  • toString:用來為類生成按宣告順序排列的所有欄位的字串表達形式

Kotlin中,只需要為你的類新增data關鍵字,以上這些必要的方法就會自動生成好,例如下面的例子,我們演示了以上三個方法的作用:

Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字
執行結果為:
Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字

equalshashCode方法會將所有在主構造方法中宣告的屬性納入考慮:

  • equals方法會檢測所有的屬性的值是否相等
  • hashCode方法會返回一個根據所有屬性生成的雜湊值

資料類和不可變性

在設計資料類時,應當儘量只使用只讀的屬性,讓資料類的例項不可變,因為如果不這樣,被用作鍵的物件在加入HashMap或者類似容器後被修改了,容器會進入一種無效的狀態。

為了讓使用不可變物件變得容易,Kotlin編譯器為它們生成了copy方法,並在copy的同時修改某些屬性的值,copy出來的副本有著單獨的宣告週期而且不會影響程式碼中引用原始例項的位置,使用方法如下:

Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字

2.2 類委託

當我們需要向一個類新增一些行為時,一般有兩種做法:

  • 繼承這個類,在子類中增加方法 這種方法的缺點是:當系統不斷演進並且基類的實現被修改或者新方法被新增進去時,你做出的關於類的行為的假設會失效。
  • 使用裝飾器模式 本質是建立一個新類,實現與原始類一樣的介面並將原來的類的例項作為一個欄位儲存。與原始類擁有同樣行為的方法不用修改,只需要直接轉發到原始類的例項。這種方法的缺點是:需要相當多的樣板程式碼。

Kotlin將委託作為一個語言級別的功能做了頭等支援。無論什麼時候實現一個介面,你都可以使用by關鍵字 將介面的實現委託到另一個物件;當需要修改某些方法的行為時,可以重寫它們,這樣你的方法就會被呼叫而不是使用生成的方法,可以保留感到滿意的委託給內部的例項中的預設實現。

Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字
執行結果為:
Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字

三、object 關鍵字

object關鍵字在多種情況下出現,它的核心理念為:這個關鍵字 定義一個類並同時建立一個例項,下面我們介紹它的三個應用場景:

  • 物件宣告 是定義單例的一種方式
  • 伴生物件 可以持有工廠方法和其它與這個類相關,但在呼叫時並不依賴類例項的方法,它們的成員可以通過類名來訪問。
  • 物件表示式 用來替代Java的匿名內部類。

3.1 物件宣告:建立單例易如反掌

Java中,單例模式通常是使用private構造方法,並且用靜態欄位來持有這個類僅有的例項。

而在Kotlin中,通過使用物件宣告功能,將類宣告與該類的單一例項宣告結合到了一起。

Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字

  • 物件宣告通過object關鍵字引入,一個物件宣告可以非常高效地以一句話來定義一個類和一個該類的變數。
  • 一個物件宣告可以包含屬性、方法、初始化語句塊等的宣告,但是 不允許宣告構造方法,這是因為物件在定義的時候就已經建立了,不需要在其他地方呼叫構造方法。
  • 物件宣告允許使用物件名加.字元的方式來呼叫方法和訪問屬性。

繼承自介面的物件宣告

物件宣告可以繼承自類和介面,這通常在你使用的框架需要去實現一個介面,但是你的實現不包含任何狀態的時候很有用。

Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字

在類中宣告物件

在類中使用物件宣告時,這樣的物件同樣只有一個單一例項:它們在每個容器類的例項中具有相同的例項。

Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字
執行結果為:
Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字

在 Java 中使用 Kotlin 物件

如果要在Java中使用Kotlin中的宣告物件,可以通過訪問靜態的INSTANCE欄位:

Kotlin 中的物件宣告
Java 的呼叫方式

3.2 伴生物件:工廠方法和靜態成員的地盤

Kotlin的類不能擁有靜態成員,作為替代,Kotlin依賴包級別函式(在大多數情形下能夠替代Java的靜態方法)和物件宣告(在其他情況下替代Java的靜態方法,同時還包括靜態欄位),在大多數情況下,推薦使用頂層函式,但是頂層函式不能訪問類的private變數。

因此,如果你需要寫一個 在沒有類例項的情況下 呼叫但是需要 訪問類內部的函式,可以將其寫成那個類中的 物件宣告的成員

在類中定義的物件之一可以使用一個特殊的關鍵字來標記 companion,如果這樣做,就獲得了直接 通過容器類名稱來訪問這個物件的方法和屬性的能力,不再需要顯示地指明物件的名稱,下面是一個基礎的示例:

Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字
執行的結果為:
Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字

使用工廠方法建立物件

下面是使用伴生物件來實現工廠方法的例子:

Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字

3.3 作為普通物件使用的伴生物件

伴生物件是一個宣告在類中的普通物件,它可以有名字,實現一個介面或者有擴充套件函式或屬性。假設我們需要在物件和JSON之間進行序列化和反序列化,可以將序列化的邏輯放在伴生物件中。

Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字
執行結果為:
Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字
在大多數情況下,通過包含伴生物件的類的名字(也就是例子中的Person類)來引用伴生物件,所以不必關心它的名字,如果省略了伴生物件的名字,預設的名字將會分配為Companion

在伴生物件中實現介面

就像其它物件宣告一樣,伴生物件也可以實現介面,可以將包含它的類的名字當做實現了該介面的物件例項來使用。

Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字
執行結果為:
Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字

伴生物件擴充套件

Kotlin 知識梳理(2) - 函式的定義與呼叫 中,我們介紹了通過擴充套件函式,可以通過程式碼庫中其它地方定義類例項呼叫的方法,但是如果你需要定義可以通過 類自身呼叫的方法,就像伴生物件方法或者Java靜態方法該怎麼辦呢?

舉例來說,如果類有一個伴生物件,可以通過在其上定義擴充套件函式來做到這一點,即類C有一個伴生物件,並且在C.Companion上定義了一個擴充套件函式func,則可以通過C.fun()來呼叫它。

下面,我們為Person類的伴生物件定義一個擴充套件函式:

Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字

當呼叫toJson就像是它是一個伴生物件定義的方法一樣,但是實際上它是作為擴充套件函式在外部定義的。而為了能夠為你的類定義擴充套件,必須在其中宣告一個物件,即使是空的。

3.4 物件表示式:改變寫法的匿名內部類

object關鍵字不僅能夠用來表明單例式的物件,還能用來宣告 匿名物件,它替代了Java中匿名內部類的用法。例如,讓我們來看看怎樣將一個典型的匿名內部類用法轉換成Kotlin

Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字
執行結果為:
Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字

將匿名物件儲存到變數中

除了去掉物件的名字外,語法與物件宣告相同的。物件表示式宣告瞭一個類並建立了該類的一個例項,但是沒有給這個類或是例項分配一個名字。通常來說,它們都是不需要名字的,因為你會將這個物件用作一個函式呼叫的引數。如果你需要給物件分配一個名字,可以將其儲存到一個變數中:

Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字

在物件表示式中修改變數的值

Java的匿名類一樣,在物件表示式中的程式碼可以訪問建立它的函式中的變數,但是與Java不同,訪問並被限制在final變數,還可以在物件表示式中修改變數的值。
Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字
執行結果為:
Kotlin 知識梳理(4)   資料類、類委託 及 object 關鍵字

更多文章,歡迎訪問我的 Android 知識梳理系列:

相關文章