您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“Kotlin1.6.20新功能Context Receivers怎么使用”的有關(guān)知識,在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
在 Kotlin 中接受者只能應(yīng)用在擴(kuò)展函數(shù)或者帶接受者 lambda 表達(dá)式中, 如下所示。
class Context { var density = 0f } // 擴(kuò)展函數(shù) inline fun Context.px2dp(value: Int): Float = value.toFloat() / density
接受者是 fun 關(guān)鍵字之后和點(diǎn)之前的類型 Context,這里隱藏了兩個(gè)知識點(diǎn)。
我們可以像調(diào)用內(nèi)部函數(shù)一樣,調(diào)用擴(kuò)展函數(shù) px2dp(),通常結(jié)合 Kotlin 作用域函數(shù) with , run , apply 等等一起使用。
with(Context()) { px2dp(100) }
在擴(kuò)展函數(shù)內(nèi)部,我們可以使用 this 關(guān)鍵字,或者隱藏關(guān)鍵字隱式訪問內(nèi)部的成員函數(shù),但是我們不能訪問私有成員
擴(kuò)展函數(shù)使用起來很方便,我們可以對系統(tǒng)或者第三方庫進(jìn)行擴(kuò)展,但是也有局限性。
只能定義一個(gè)接受者,因此限制了它的可組合性,如果有多個(gè)接受者只能當(dāng)做參數(shù)傳遞。比如我們調(diào)用 px2dp() 方法的同時(shí),往 logcat 和 file 中寫入日志。
class LogContext { fun logcat(message: Any){} } class FileContext { fun writeFile(message: Any) {} } fun printf(logContext: LogContext, fileContext: FileContext) { with(Context()) { val dp = px2dp(100) logContext.logcat("print ${dp} in logcat") fileContext.writeFile("write ${dp} in file") } }
在 Kotlin 中接受者只能應(yīng)用在擴(kuò)展函數(shù)或者帶接受者 lambda 表達(dá)式中,卻不能在普通函數(shù)中使用,失去了靈活性
Context Receivers 的出現(xiàn)帶來新的可能性,它通過了組合的方式,將多個(gè)上下文接受者合并在一起,靈活性更高,應(yīng)用范圍更廣。
Context Receivers 用于表示一個(gè)基本約束,即在某些情況下需要在某些范圍內(nèi)才能完成的事情,它更加的靈活,可以通過組合的方式,組織上下文,將系統(tǒng)或者第三方類組合在一起,實(shí)現(xiàn)更多的功能。
如果想在項(xiàng)目中使用 Context Receivers,需要將 Kotlin 插件升級到 1.6.20 ,并且在項(xiàng)目中開啟才可以使用。
plugins { id 'org.jetbrains.kotlin.jvm' version '1.6.20' } // ...... kotlinOptions { freeCompilerArgs = ["-Xcontext-receivers"] }
當(dāng)我們完成上述配置之后,就可以在項(xiàng)目中使用 Context Receivers,現(xiàn)在我們將上面的案例改造一下。
context(LogContext, FileContext) fun printf() { with(Context()) { val dp = px2dp(100) logContext.logcat("print ${dp} in logcat") fileContext.writeFile("write ${dp} in file") } }
我們在 printf() 函數(shù)上,使用 context() 關(guān)鍵字,在 context() 關(guān)鍵字括號中,聲明上下文接收者類型的列表,多個(gè)類型用逗號分隔。但是列出的類型不允許重復(fù),它們之間不允許有子類型關(guān)系。
通過 context() 關(guān)鍵字來限制它的作用范圍,在這個(gè)函數(shù)中,我們可以調(diào)用上下文 LogContext 、 FileContext 內(nèi)部的方法,但是使用的時(shí)候,只能通過 Kotlin 作用域函數(shù)嵌套來傳遞多個(gè)接受者,也許在未來可能會(huì)提供更加優(yōu)雅的方式。
with(LogContext()) { with(FileContext()) { printf("I am DHL") } }
如果我們在 LogContext 和 FileContext 中聲明了多個(gè)相同名字的變量或者函數(shù),我們只能通過 this@Lable 語句來解決這個(gè)問題。
context(LogContext, FileContext) fun printf(message: String) { logcat("print message in logcat ${this@LogContext.name}") writeFile("write message in file ${this@FileContext.name}") }
正如你所見,在 LogContext 和 FileContext 中都有一個(gè)名為 name 的變量,我們只能通過 this@Lable 語句來訪問,但是這樣會(huì)引入一個(gè)新的問題,如果有大量的同名的變量或者函數(shù),會(huì)導(dǎo)致 this 關(guān)鍵字分散到處都是,造成可讀性很差。所以我們可以通過接口隔離的方式,來解決這個(gè)問題。
interface LogContextInterface{ val logContext:LogContext } interface FileContextInterface{ val fileContext:FileContext } context(LogContextInterface, FileContextInterface) fun printf(message: String) { logContext.logcat("print message in logcat ${logContext.name}") fileContext.writeFile("write message in file ${fileContext.name}") }
通過接口隔離的方式,我們就可以解決 this 關(guān)鍵字導(dǎo)致的可讀性差的問題,使用的時(shí)候需要實(shí)例化接口。
val logContext = object : LogContextInterface { override val logContext: LogContext = LogContext() } val fileContext = object : FileContextInterface { override val fileContext: FileContext = FileContext() } with(logContext) { with(fileContext) { printf("I am DHL") } }
當(dāng)我們重寫帶有上下文接受者的函數(shù)時(shí),必須聲明為相同類型的上下文接受者。
interface Canvas interface Shape { context(Canvas) fun draw() } class Circle : Shape { context(Canvas) override fun draw() { } }
我們重寫了 draw() 函數(shù),聲明的上下文接受者必須是相同的,Context Receivers 不僅可以作用在擴(kuò)展函數(shù)、普通函數(shù)上,而且還可以作用在類上。
context(LogContextInterface, FileContextInterface) class LogHelp{ fun printf(message: String) { logContext.logcat("print message in logcat ${logContext.name}") fileContext.writeFile("write message in file ${fileContext.name}") } }
在類 LogHelp 上使用了 context() 關(guān)鍵字,我們就可以在 LogHelp 范圍內(nèi)任意的地方使用 LogContext 或者 FileContex。
val logHelp = with(logContext) { with(fileContext) { LogHelp() } } logHelp.printf("I am DHL")
Context Receivers 除了作用在擴(kuò)展函數(shù)、普通函數(shù)、類上,還可以作用在屬性 getter 和 setter 以及 lambda 表達(dá)式上。
context(View) val Int.dp get() = this.toFloat().dp // lambda 表達(dá)式 fun save(block: context(LogContextInterface) () -> Unit) { }
最后我們來看一下,來自社區(qū) Context Receivers 實(shí)踐的案例,擴(kuò)展 Json 工具類。
fun json(build: JSONObject.() -> Unit) = JSONObject().apply { build() } context(JSONObject) infix fun String.by(build: JSONObject.() -> Unit) = put(this, JSONObject().build()) context(JSONObject) infix fun String.by(value: Any) = put(this, value) fun main() { val json = json { "name" by "Kotlin" "age" by 10 "creator" by { "name" by "JetBrains" "age" by "21" } } }
“Kotlin1.6.20新功能Context Receivers怎么使用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。