展函數(shù)的作用域)
核心規(guī)則一句話擴(kuò)展函數(shù)定義在哪里決定了它在哪里可見、在哪里可調(diào)。Kotlin 里有三種定義位置作用域行為完全不同。三種定義位置① 頂層擴(kuò)展——最常見到處可用// StringUtils.kt 文件頂層 fun String.shout(): String uppercase() !!! // 任何地方import 后都能調(diào) hello.shout()② 類內(nèi)擴(kuò)展成員擴(kuò)展——作用域受限的“特權(quán)函數(shù)”class Density { fun fontScale: Float 1f // 定義在 Density 類內(nèi)部的 String 擴(kuò)展 fun String.scaleByFont(): Float this.length * fontScale }關(guān)鍵限制成員擴(kuò)展只能在這個類的內(nèi)部或其子類調(diào)用with(density) { abc.scaleByFont() // ? this 是 Density在作用域內(nèi) } // abc.scaleByFont() // ? 編譯錯誤外面解析不到這個擴(kuò)展這就是為什么 minHeight.toPx() 必須包在 with(density) { } 里——toPx() 是 Density 的成員擴(kuò)展出了這個作用域就不存在。③ object/companion 內(nèi)擴(kuò)展——只在那個對象作用域內(nèi)object Validators { fun String.isValidEmail(): Boolean matches(Regex(^[\\w.-][\\w.-]$)) } with(Validators) { ab.com.isValidEmail() // ? }成員擴(kuò)展的“雙接收者”機(jī)制成員擴(kuò)展同時攜帶兩個接收者class Density { val density: Float 2.75f fun Dp.toPx(): Float { // └── extension receiverDpthis 也指向它 return value * density // └────── 這里隱式訪問的是 dispatch receiver 的屬性 } }兩個接收者沖突時extension receiver 優(yōu)先需要 dispatch receiver 時用標(biāo)簽限定class Outer { val size 10 fun String.f() { val ext length // String 的成員優(yōu)先 val disp thisOuter.size // 顯式拿 Outer 的 } }作用域如何決定“能調(diào)哪個擴(kuò)展”Kotlin 解析 a.foo() 時的優(yōu)先級1. 類自身/父類的成員函數(shù) ← 永遠(yuǎn)贏過擴(kuò)展 2. 當(dāng)前作用域內(nèi)可見的擴(kuò)展 ├─ 本文件/本類內(nèi)的定義 ← 就近優(yōu)先 ├─ 顯式 import 的擴(kuò)展 └─ 同包頂層的擴(kuò)展同名沖突時更內(nèi)層作用域的擴(kuò)展勝出fun String.info() 頂層版本 class A { fun String.info() A 內(nèi)版本 fun test(s: String) { s.info() // 輸出 A 內(nèi)版本——內(nèi)層作用域優(yōu)先 } }Compose 大量利用這一點Dp.toPx()、DrawScope 里的 drawCircle()、Modifier.Node 里的各種操作——不是能力被藏起來了而是語義上必須綁定上下文。像素?fù)Q算離開 Density 就沒有意義編譯器通過作用域強(qiáng)制你提供它。擴(kuò)展函數(shù)沒有“注入到類里”它始終是外掛的靜態(tài)函數(shù)定義位置頂層 / 類內(nèi) / 對象內(nèi)決定它的可見作用域其中成員擴(kuò)展只能在宿主類的作用域內(nèi)調(diào)用——這是 Kotlin 用作用域模擬“上下文依賴 API”的核心手法。