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