Kotlin學習 6 -- 泛型和委托

本篇文章主要介紹以下幾個知識點:

SUMMER DAY (圖片來源于網絡)

1. 泛型的基本用法

泛型允許在不指定具體類型的情況下進行編程,這樣會有更好的擴展性焚廊。

泛型主要有兩種定義方式:定義泛型類 和 定義泛型方法。使用的語法結構都是<T>伍派,當然括號內的 T 可以用任何英文字母或單詞。

如定義個泛型類:

class MyClass<T> {
    fun method(param: T): T {
        return param
    }
}

在調用 MyClass 類和 method() 方法時就可以將泛型指定成具體的類型:

fun main() {
    // 這邊把泛型指定成 Int 類型
    val mClass = MyClass<Int>()
    val result = mClass.method(123)
    println(result)
}

若不想定義泛型類,只想定義一個泛型方法截粗,可改為:

class MyClass {
    fun <T> method(param: T): T {
        return param
    }
}

此時調用方式也要修改:

fun main() {
    val mClass = MyClass()
    // val result = mClass.method<Int>(123)
    // 由于 Kotlin 的推導機制,這邊的 Int 可以省略 
    val result = mClass.method(123)
    println(result)
}

Kotlin 還允許對泛型類型進行限制鸵隧,通過指定上界的方式對泛型的類型進行約束绸罗,如將設置為 Number 類型:

class MyClass {
    fun <T : Number> method(param: T): T {
        return param
    }
}

注:在默認情況下,泛型的上界是 Any? 豆瘫,即所有的泛型都是可以指定成可空類型的珊蟀,若想讓泛型的類型不可空谓着,手動指定成 Any 即可蘸际。


回顧前面學習高階函數時的一個例子:

// 這個 build 函數和 apply 函數的作用是一樣的勒庄,
// 但 build 函數只能作用在 StringBuilder 中抛腕,而 apply 可以作用在所有類上
fun StringBuilder.build(block: StringBuilder.() -> Unit): StringBuilder {
    block()
    return this
}

要讓上面的 build 實現和 apply 一樣的功能蓖谢,可使用泛型修改如下:

fun <T> T.build(block: T.() -> Unit): T {
    block()
    return this
}

這樣就可以和 apply 一樣去使用 build 函數了逗宁。

2. 類委托和委托屬性

委托是一種設計模式倡怎,其基本理念是:操作對象自己不會去處理某段邏輯踢械,而是把工作委托給另外一個輔助對象去處理趟薄。

Kotlin 中將委托功能分為:類委托和委托屬性绽诚。(注:Java 對委托沒有語言層面的實現)

  • 類委托:將一個類的具體實現委托給另一個類去完成。

下面舉個栗子,定義一個 MySet 類實現 Set 接口:

// Set 數據結構和 list 不同的是所存儲的數據是無序的恩够,不能存儲重復的數據
// Set 接口卒落,要使用它需要使用它具體的實現類,如 HashSet

// 這邊在構造函數中接收了一個 HashSet 參數蜂桶,相當于一個輔助對象
// 然后在 Set 接口中所有的方法實現中儡毕,都沒有進行自己的實現,
// 而是調用了輔助對象 HashSet 中相應的方法扑媚,這其實就是一種委托模式
class MySet<T>(val helperSet: HashSet<T>) : Set<T> {
    override val size: Int get() = helperSet.size

    override fun contains(element: T) = helperSet.contains(element)

    override fun containsAll(elements: Collection<T>) = helperSet.containsAll(elements)

    override fun isEmpty() = helperSet.isEmpty()

    override fun iterator() = helperSet.iterator()
}

委托模式的意義主要在于:大部分的方法調用輔助對象中的方法腰湾,少部分的方法由自己實現,甚至加入一些獨有方法疆股。(如果都調用輔助對象中的方法那還不如直接使用輔助對象得了)

不過费坊,上面的寫法有一定的弊端,當輔助對象中存在很多方法時旬痹,每個都去這樣調用輔助對象中的相應方法實現附井,那就很繁瑣了。還好 Kotlin 中可以通過類委托來解決两残。

Kotlin 中委托使用的關鍵字是 by永毅,在接口聲明的后面使用 by 關鍵字,再接上受委托的輔助對象即可:

class MySet<T>(val helperSet: HashSet<T>) : Set<T> by helperSet{
    // 通過類委托磕昼,這邊可以免去之前的一大堆模板式代碼了卷雕,實現的效果卻是一樣的
}

比如對其某個方法重新實現节猿、新增自己的方法:

class MySet<T>(val helperSet: HashSet<T>) : Set<T> by helperSet {

    // 新增一個 sayHello 方法
    fun sayHello() = println("Hello Wonderful")

    // 重寫 isEmpty 方法
    override fun isEmpty() = false
}

  • 委托屬性:將一個屬性(字段)的具體實現委托給另一個類去完成票从。

委托屬性的語法結構如下:

class MyClass {
    // by 關鍵字左邊 p 屬性的具體實現委托給了 Delegate 類去完成
    var p by Delegate()
}

接著還得對 Delegate 類進行具體的實現,如下:

class Delegate {
    var propValue: Any? = null

    // 需要使用 operator 關鍵字聲明
    // KProperty<*> 是 Kotlin 中的一個屬性操作類滨嘱,可用于獲取各種屬性相關的值峰鄙,
    // 即使用不著也必須在方法參數上聲明
    // <*> 表示不關心泛型的具體類型(類似于 Java 中的<?>)
    operator fun getValue(mClass: MyClass, prop: KProperty<*>): Any? {
        return propValue
    }

    operator fun setValue(mClass: MyClass, prop: KProperty<*>, value: Any?) {
        propValue = value
    }
}

這樣當調用 p 屬性時會自動調用 Delegate 類的 getValue() 方法,當給 p 屬性賦值時會自動調用 Delegate 類的 setValue() 方法太雨。

3. 實現一個自己的 lazy 函數

有時候初始化變量時吟榴,會用一種懶加載技術,把想要延遲執(zhí)行的代碼放到 by lazy 代碼塊中囊扳,這樣代碼塊中的代碼在一開始時就不會執(zhí)行吩翻,只有當變量首次被調用時代碼塊中的代碼才會執(zhí)行。

by lazy 的語法結構如下:

// 這里 by lazy 不是連在一起的锥咸,by 是關鍵字狭瞎,lazy 是一個高階函數
// 在 lazy 函數中會創(chuàng)建并返回一個 Delegate 對象
val p by lazy { ... }

下面舉個栗子來實現一個自己的 lazy 函數:

class Later<T>(val block: () -> T) {
    // 這里定義了個 Later 類,并將他指定成泛型類
}

接著在 Later 類中實現 getValue() 方法:

class Later<T>(val block: () -> T) {
    var value: Any? = null

    operator fun getValue(any: Any?, prop: KProperty<*>): T {
        if (value == null) {
            value = block()
        }
        return value as T
    }

    // 由于懶加載技術不會對屬性進行賦值搏予,無需實現 setValue 方法
}

為了讓它用法更加類似于 lazy 函數熊锭,再定義個頂層函數:

// 用于創(chuàng)建 Later 類的實例,并將接收的函數類型參數傳給 Later 類的構造函數
fun <T> later(block: () -> T) = Later(block)

這樣,自定義的 later 懶加載函數就完成了碗殷,用法如下:

val p by later {
    // something to do
}

本篇文章就介紹到這精绎。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市锌妻,隨后出現的幾起案子代乃,更是在濱河造成了極大的恐慌,老刑警劉巖仿粹,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件襟己,死亡現場離奇詭異,居然都是意外死亡牍陌,警方通過查閱死者的電腦和手機擎浴,發(fā)現死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來毒涧,“玉大人贮预,你說我怎么就攤上這事∑踅玻” “怎么了仿吞?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長捡偏。 經常有香客問我唤冈,道長,這世上最難降的妖魔是什么银伟? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任你虹,我火速辦了婚禮,結果婚禮上彤避,老公的妹妹穿的比我還像新娘傅物。我一直安慰自己,他們只是感情好琉预,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布董饰。 她就那樣靜靜地躺著,像睡著了一般圆米。 火紅的嫁衣襯著肌膚如雪卒暂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天娄帖,我揣著相機與錄音也祠,去河邊找鬼。 笑死块茁,一個胖子當著我的面吹牛齿坷,可吹牛的內容都是我干的桂肌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼永淌,長吁一口氣:“原來是場噩夢啊……” “哼崎场!你這毒婦竟也來了?” 一聲冷哼從身側響起遂蛀,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤谭跨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后李滴,有當地人在樹林里發(fā)現了一具尸體螃宙,經...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年所坯,在試婚紗的時候發(fā)現自己被綠了谆扎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡芹助,死狀恐怖堂湖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情状土,我是刑警寧澤无蜂,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站蒙谓,受9級特大地震影響斥季,放射性物質發(fā)生泄漏。R本人自食惡果不足惜累驮,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一酣倾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧慰照,春花似錦灶挟、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽箱叁。三九已至墅垮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間耕漱,已是汗流浹背算色。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留螟够,地道東北人灾梦。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓峡钓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親若河。 傳聞我的和親對象是個殘疾皇子能岩,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354