你應(yīng)該知道的kotlin實用技巧

前言

眾所周知单默,kotlin是google力推的用以取代java的android開發(fā)語言
kotlin使用起來比較方便迅脐,同時有許多語法糖
本文主要講解了一些比較實用的kotlin技巧

自定義圓角矩形

在項目中,我們常常要定義圓角矩形背景捌肴,一般是用自定義drawable實現(xiàn)的
但是圓角矩形的背景與圓角常常會有細微的變化,而一旦變化我們又要新創(chuàng)建一個drawable文件
這樣就會導(dǎo)致文件爆炸的問題

我們可以利用kotlin的擴展函數(shù),來實現(xiàn)簡單方便的圓角矩形背景

fun View.setRoundRectBg(color: Int = Color.WHITE, cornerRadius: Int = 15.dp) {
    background = GradientDrawable().apply {
        setColor(color)
        setCornerRadius(cornerRadius.toFloat())
    }
}

對于需要自定義背景的View,直接調(diào)用setRoundRectBg即可椅邓,簡單方便

reified使用

reified,kotlin中的泛型實化關(guān)鍵字,使抽象的東西更加具體或真實昧狮。
我們舉兩個例子來看看怎么使用reified

startActivity例子

我們一般startActivity是這樣寫的

startActivity(context, NewActivity::class.java)  

我們利用reified定義一個擴展函數(shù)

// Function
inline fun <reified T : Activity> Activity.startActivity(context: Context) {
    startActivity(Intent(context, T::class.java))
}

// Caller
startActivity<NewActivity>(context)

使用 reified景馁,通過添加類型傳遞簡化泛型參數(shù)
這樣就不用手動傳泛型的類型過去了

Gson解析例子

我們首先看下一般我們使用gson解析json是怎么做的
在Java序列化庫(如Gson)中,當(dāng)您想要反序列化該JSON字符串時逗鸣,您最終必須將Class對象作為參數(shù)傳遞合住,以便Gson知道您想要的類型。

User user = new Gson().fromJson(getJson(), User.class)

現(xiàn)在撒璧,讓我們一起展示reified類型實化參數(shù)的魔法 我們將創(chuàng)建一個非常輕量級的擴展函數(shù)來包裝Gson方法:

inline fun <reified T> Gson.fromJson(json: String) = 
        fromJson(json, T::class.java)

現(xiàn)在透葛,在我們的Kotlin代碼中,我們可以反序列化JSON字符串卿樱,甚至根本不需要傳遞類型信息僚害!

val user: User = Gson().fromJson(json)

Kotlin根據(jù)它的用法推斷出類型 - 因為我們將它分配給User類型的變量,Kotlin使用它作為fromJson()的類型參數(shù)

kotin接口支持SAM轉(zhuǎn)換

什么是SAM轉(zhuǎn)換繁调?可能有的同學(xué)還不太了解萨蚕,這里先科普一下:

SAM 轉(zhuǎn)換,即 Single Abstract Method Conversions蹄胰,就是對于只有單個非默認抽象方法接口的轉(zhuǎn)換 —— 對于符合這個條件的接口(稱之為 SAM Type )岳遥,在 Kotlin 中可以直接用 Lambda 來表示 —— 當(dāng)然前提是 Lambda 的所表示函數(shù)類型能夠跟接口的中方法相匹配。

在Kotlin1.4之前裕寨,Kotlin是不支持Kotlin的SAM轉(zhuǎn)換的寒随,只支持Java SAM轉(zhuǎn)換,官方給出的的解釋是:是 Kotlin 本身已經(jīng)有了函數(shù)類型和高階函數(shù)帮坚,不需要在去SAM轉(zhuǎn)化妻往。 這個解釋開發(fā)者并不買賬,如果你用過Java Lambda和Fuction Interface试和。當(dāng)你切換到Kotlin時讯泣,就會很懵逼≡暮罚看來Kotlin是意識到了這個好渠,或者是看到開發(fā)者的反饋昨稼,終于支持了。

在1.4之前拳锚,只能傳遞一個對象假栓,是不支持Kotlin SAM的,而在1.4之后霍掺,可以支持Kotlin SAM,但是用法有一丟丟變化匾荆。interface需要使用fun關(guān)鍵字聲明。使用fun關(guān)鍵字標(biāo)記接口后杆烁,只要將此類接口作為參數(shù)牙丽,就可以將lambda作為參數(shù)傳遞。

// 注意需用fun 關(guān)鍵字聲明
fun interface Action {
    fun run()
}

fun runAction(a: Action) = a.run()

fun main(){
    // 1.4之前兔魂,只能使用object
    runAction(object : Action{
        override fun run() {
            println("run action")
        }
    })
     // 1.4-M1支持SAM,OK
    runAction {
        println("Hello, Kotlin 1.4!")
    }
}

委托

有時候烤芦,完成一些工作的方法是將它們委托給別人。這里不是在建議您將自己的工作委托給朋友去做析校,而是在說將一個對象的工作委托給另一個對象构罗。

當(dāng)然,委托在軟件行業(yè)不是什么新鮮名詞智玻。委托 (Delegation) 是一種設(shè)計模式绰播,在該模式中,對象會委托一個助手 (helper) 對象來處理請求尚困,這個助手對象被稱為代理蠢箩。代理負責(zé)代表原始對象處理請求,并使結(jié)果可用于原始對象事甜。

類委托

舉個例子谬泌,當(dāng)我們要實現(xiàn)一個增強版的ArrayList,支持恢復(fù)最后一次刪除的item

實現(xiàn)這個用例的一種方式,是繼承 ArrayList 類逻谦。由于新的類繼承了具體的 ArrayList 類而不是實現(xiàn) MutableList 接口掌实,因此它與 ArrayList 的實現(xiàn)高度耦合。
如果只需要覆蓋 remove() 函數(shù)來保持對已刪除項目的引用邦马,并將 MutableList 的其余空實現(xiàn)委托給其他對象贱鼻,那該有多好啊。為了實現(xiàn)這一目標(biāo)滋将,Kotlin 提供了一種將大部分工作委托給一個內(nèi)部 ArrayList 實例并且可以自定義其行為的方式邻悬,并為此引入了一個新的關(guān)鍵字: by。

<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class ListWithTrash <T>(private val innerList: MutableList<T> = ArrayList<T>()) : MutableCollection<T> by innerList {
    var deletedItem : T? = null
    override fun remove(element: T): Boolean {
           deletedItem = element
            return innerList.remove(element)
    }
    fun recover(): T? {
        return deletedItem
    }
}

by 關(guān)鍵字告訴 Kotlin 將 MutableList 接口的功能委托給一個名為 innerList 的內(nèi)部 ArrayList随闽。通過橋接到內(nèi)部 ArrayList 對象方法的方式父丰,ListWithTrash 仍然支持 MutableList 接口中的所有函數(shù)。與此同時掘宪,現(xiàn)在您可以添加自己的行為了蛾扇。

屬性委托

除了類代理攘烛,您還可以使用 by 關(guān)鍵字進行屬性代理。通過使用屬性代理镀首,代理類會負責(zé)處理對應(yīng)屬性 get 與 set 函數(shù)的調(diào)用坟漱。這一特性在您需要在其他對象間復(fù)用 getter/setter 邏輯時十分有用,同時也能讓您可以輕松地對簡單支持字段的功能進行擴展

舉個例子更哄,利用委托屬性可以封裝SharedPreference
將數(shù)據(jù)存儲操作委托給代理類有幾個好處
1.則精簡了代碼芋齿,方便了存儲與讀取調(diào)用
2.與SP進行了解耦,后續(xù)如果要替換存儲庫竖瘾,只需要修改代理類即可

調(diào)用如下:

object Pref: PreferenceHolder() {
    var isFirstInstall: Boolean by bindToPreferenceField(false)
    var time: Long? by bindToPreferenceFieldNullable()
}

具體實現(xiàn)可見:SharedPreferences用Kotlin應(yīng)該這樣寫

帶狀態(tài)的LiveData

目前我們在開發(fā)的過程中越來越多的使用MVVM模式與ViewModel
我們也常常用LiveData來標(biāo)識網(wǎng)絡(luò)請求狀態(tài)
我們需要定義請求開始沟突,請求成功花颗,請求失敗捕传,三個LiveData

這其實也是很冗余重復(fù)的代碼,因此我們可以進行一定的封裝扩劝,封裝一個帶狀態(tài)的LiveData

定義如下:

typealias StatefulLiveData<T> = LiveData<RequestState<T>>
typealias StatefulMutableLiveData<T> = MutableLiveData<RequestState<T>>

@MainThread
inline fun <T> StatefulLiveData<T>.observeState(
    owner: LifecycleOwner,
    init: ResultBuilder<T>.() -> Unit
) {
    val result = ResultBuilder<T>().apply(init)

    observe(owner) { state ->
        when (state) {
            is RequestState.Loading -> result.onLading.invoke()
            is RequestState.Success -> result.onSuccess(state.data)
            is RequestState.Error -> result.onError(state.error)
        }
    }
}

使用如下

val data = StatefulMutableLiveData<String>()
viewModel.data.observeState(viewLifecycleOwner) {
            onLading = {
                //loading
            }
            onSuccess = { data ->
                //success
            }
            onError = { exception ->
                //error
            }
        }

通過以上封裝庸论,可以比較優(yōu)雅簡潔的封裝網(wǎng)絡(luò)請求的loading,success,error狀態(tài),精簡了代碼棒呛,結(jié)構(gòu)也比較清晰

DSL

DSL(domain specific language)聂示,即領(lǐng)域?qū)S谜Z言:專門解決某一特定問題的計算機語言,比如大家耳熟能詳?shù)?SQL 和正則表達式簇秒。
但是鱼喉,如果為解決某一特定領(lǐng)域問題就創(chuàng)建一套獨立的語言,開發(fā)成本和學(xué)習(xí)成本都很高趋观,因此便有了內(nèi)部 DSL 的概念扛禽。所謂內(nèi)部 DSL,便是使用通用編程語言來構(gòu)建 DSL皱坛。比如编曼,本文提到的 Kotlin DSL,我們?yōu)?Kotlin DSL 做一個簡單的定義:

“使用 Kotlin 語言開發(fā)的剩辟,解決特定領(lǐng)域問題掐场,具備獨特代碼結(jié)構(gòu)的 API 》妨裕”

舉個例子熊户,我們使用TabLayout時,如果要為他添加監(jiān)聽吭服,需要實現(xiàn)以下3個方法

override fun onTabReselected(tab: TabLayout.Tab?){

}

override fun onTabUnselected(tab: TabLayout.Tab?){

}

override fun onTabSelected(tab: TabLayout.Tab?){

}

其實我們一般只會用到onTabSelected方法敏弃,其余兩個一般是空實現(xiàn)
我們利用DSL對OnTabSelectedListener進行封裝,即可避免寫不必要的空實現(xiàn)代碼

具體實現(xiàn)如下:

private typealias OnTabCallback = (tab: TabLayout.Tab?) -> Unit

class OnTabSelectedListenerBuilder : TabLayout.OnTabSelectedListener {

    private var onTabReselectedCallback: OnTabCallback? = null
    private var onTabUnselectedCallback: OnTabCallback? = null
    private var onTabSelectedCallback: OnTabCallback? = null

    override fun onTabReselected(tab: TabLayout.Tab?) =
            onTabReselectedCallback?.invoke(tab) ?: Unit

    override fun onTabUnselected(tab: TabLayout.Tab?) =
            onTabUnselectedCallback?.invoke(tab) ?: Unit

    override fun onTabSelected(tab: TabLayout.Tab?) =
            onTabSelectedCallback?.invoke(tab) ?: Unit

    fun onTabReselected(callback: OnTabCallback) {
        onTabReselectedCallback = callback
    }

    fun onTabUnselected(callback: OnTabCallback) {
        onTabUnselectedCallback = callback
    }

    fun onTabSelected(callback: OnTabCallback) {
        onTabSelectedCallback = callback
    }

}

fun registerOnTabSelectedListener(function: OnTabSelectedListenerBuilder.() -> Unit) =
        OnTabSelectedListenerBuilder().also(function)


定義DSL的一般步驟:

  • 1.先定義一個類去實現(xiàn)回調(diào)接口噪馏,并且實現(xiàn)它的回調(diào)方法麦到。
  • 2.觀察回調(diào)方法的參數(shù)抱究,提取成一個函數(shù)類型(function type),并且按照需要使用類型別名給函數(shù)類型起一個別稱虚汛,并且用私有修飾痛垛。
  • 3.在類里面聲明一些可空的函數(shù)類型的可變(var)私有成員變量,并且在回調(diào)函數(shù)中拿到對應(yīng)的變量實現(xiàn)它的invoke函數(shù)粹淋,傳入對應(yīng)的參數(shù)吸祟。
  • 4.在類中定義一些跟回調(diào)接口一樣名字,但是參數(shù)是對應(yīng)的函數(shù)類型的函數(shù)桃移,并且將函數(shù)類型賦值給當(dāng)前類的對應(yīng)的成員變量屋匕。
  • 5.定義一個成員函數(shù),參數(shù)是一個帶有我們定好那個類的接受者對象并且返回Unit的Lambda表達式借杰,在函數(shù)里創(chuàng)建相應(yīng)的對象过吻,并且使用also函數(shù)把Lambda表達式傳進去。

調(diào)用如下:

tabLayout.addOnTabSelectedListener(registerOnTabSelectedListener {
    onTabSelected { vpOrder.currentItem = it?.position ?: 0 }
})

如上蔗衡,就可以避免寫一些不必要的空實現(xiàn)代碼了

本文在開源項目:https://github.com/Android-Alvin/Android-LearningNotes 中已收錄纤虽,里面包含不同方向的自學(xué)編程路線、面試題集合/面經(jīng)绞惦、及系列技術(shù)文章等逼纸,資源持續(xù)更新中...

作者:RicardoMJiang
鏈接:https://juejin.cn/post/6921337734216810504

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市济蝉,隨后出現(xiàn)的幾起案子杰刽,更是在濱河造成了極大的恐慌,老刑警劉巖王滤,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贺嫂,死亡現(xiàn)場離奇詭異,居然都是意外死亡淑仆,警方通過查閱死者的電腦和手機涝婉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蔗怠,“玉大人墩弯,你說我怎么就攤上這事∧洌” “怎么了渔工?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長桥温。 經(jīng)常有香客問我引矩,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任旺韭,我火速辦了婚禮氛谜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘区端。我一直安慰自己值漫,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布织盼。 她就那樣靜靜地躺著杨何,像睡著了一般。 火紅的嫁衣襯著肌膚如雪沥邻。 梳的紋絲不亂的頭發(fā)上危虱,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機與錄音唐全,去河邊找鬼埃跷。 笑死,一個胖子當(dāng)著我的面吹牛芦瘾,可吹牛的內(nèi)容都是我干的捌蚊。 我是一名探鬼主播集畅,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼近弟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了挺智?” 一聲冷哼從身側(cè)響起祷愉,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎赦颇,沒想到半個月后二鳄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡媒怯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年订讼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扇苞。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡欺殿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鳖敷,到底是詐尸還是另有隱情脖苏,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布定踱,位于F島的核電站棍潘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜亦歉,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一恤浪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧肴楷,春花似錦资锰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至濒募,卻和暖如春鞭盟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瑰剃。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工齿诉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人晌姚。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓粤剧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親挥唠。 傳聞我的和親對象是個殘疾皇子抵恋,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 前言 人生苦多,快來 Kotlin 宝磨,快速學(xué)習(xí)Kotlin弧关! 什么是Kotlin? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,215評論 9 118
  • 夜鶯2517閱讀 127,720評論 1 9
  • 版本:ios 1.2.1 亮點: 1.app角標(biāo)可以實時更新天氣溫度或選擇空氣質(zhì)量唤锉,建議處女座就不要選了世囊,不然老想...
    我就是沉沉閱讀 6,898評論 1 6
  • 我是一名過去式的高三狗,很可悲窿祥,在這三年里我沒有戀愛株憾,看著同齡的小伙伴們一對兒一對兒的,我的心不好受晒衩。怎么說呢嗤瞎,高...
    小娘紙閱讀 3,388評論 4 7
  • 這些日子就像是一天一天在倒計時 一想到他走了 心里就是說不出的滋味 從幾個月前認識他開始 就意識到終究會發(fā)生的 只...
    栗子a閱讀 1,621評論 1 3