Skip to content

Kotlin 扩展函数与扩展属性详解 🔧

Android 面试必考 Kotlin 扩展函数,包含扩展函数原理、扩展属性、作用域、优先级等核心知识点


目录

  1. 扩展函数定义
  2. 扩展属性
  3. 扩展函数原理(静态方法)
  4. 扩展函数与成员函数优先级
  5. 扩展函数作用域
  6. 常见扩展函数示例
  7. 面试考点汇总

1. 扩展函数定义

1.1 基础语法

扩展函数允许为现有类添加新方法,而无需继承或修改原类:

kotlin
// 基本语法:接收者类型。函数名
fun String.addPrefix(prefix: String): String {
    return "$prefix$this"
}

// 使用
val result = "Kotlin".addPrefix("Hello ")  // "Hello Kotlin"

// 扩展函数可以访问接收者的 public/protected 成员
fun String.getLastChar(): Char {
    return this[this.length - 1]  // 访问 String 的 get 方法
}

val last = "Kotlin".getLastChar()  // 'n'

1.2 接收者(Receiver)

kotlin
// this 指向接收者对象
fun String.addSuffix(suffix: String): String {
    return this + suffix  // this 可以省略
}

// this 显式使用
fun String.addPrefixAndSuffix(prefix: String, suffix: String): String {
    return "$prefix${this}$suffix"
}

// 带参数的扩展函数
fun String.repeat(n: Int): String {
    return buildString {
        repeat(n) {
            append(this@repeat)  // this@repeat 明确指向接收者
        }
    }
}

val result = "Ha".repeat(3)  // "HaHaHa"

1.3 可空接收者

kotlin
// 为可空类型定义扩展函数
fun String?.isNullOrEmpty(): Boolean {
    return this == null || this.isEmpty()
}

fun String?.isNullOrBlank(): Boolean {
    return this == null || this.isBlank()
}

// 使用
val str1: String? = null
val str2: String? = ""
val str3: String? = "  "

println(str1.isNullOrEmpty())  // true
println(str2.isNullOrEmpty())  // true
println(str3.isNullOrBlank())  // true

// 安全调用扩展函数
val length = str1?.length ?: 0  // 0

1.4 泛型扩展函数

kotlin
// 泛型扩展函数
fun <T> List<T>.safeGet(index: Int): T? {
    return if (index in indices) this[index] else null
}

// 使用
val list = listOf(1, 2, 3)
val item = list.safeGet(5)  // null

// 带约束的泛型扩展
fun <T : Comparable<T>> List<T>.maxOrNull(): T? {
    return this.maxOrNull()
}

// 接收者是泛型
fun <T> T?.letOrNull(block: (T) -> Unit) {
    this?.let(block)
}

1.5 扩展函数实战

kotlin
// Android 常用扩展
// 1. View 扩展
fun View.visible() {
    visibility = View.VISIBLE
}

fun View.gone() {
    visibility = View.GONE
}

fun View.invisible() {
    visibility = View.INVISIBLE
}

fun View.toggleVisibility() {
    visibility = if (visibility == View.VISIBLE) View.GONE else View.VISIBLE
}

// 2. Context 扩展
fun Context.showToast(message: String, duration: Int = Toast.LENGTH_SHORT) {
    Toast.makeText(this, message, duration).show()
}

fun Context.dpToPx(dp: Float): Int {
    return (dp * resources.displayMetrics.density).toInt()
}

fun Context.pxToDp(px: Int): Float {
    return px / resources.displayMetrics.density
}

fun Context.openUrl(url: String) {
    val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
    startActivity(intent)
}

fun Context.getColorCompat(@ColorRes id: Int): Int {
    return ContextCompat.getColor(this, id)
}

// 3. Fragment 扩展
fun Fragment.showToast(message: String) {
    Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
}

fun Fragment.navigate(@IdRes destId: Int, args: Bundle? = null) {
    findNavController().navigate(destId, args)
}

// 4. Lifecycle 扩展
fun LifecycleOwner.observeLifecycle(
    onCreate: () -> Unit = {},
    onStart: () -> Unit = {},
    onResume: () -> Unit = {},
    onPause: () -> Unit = {},
    onStop: () -> Unit = {},
    onDestroy: () -> Unit = {}
) {
    lifecycle.addObserver(object : LifecycleObserver {
        @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
        fun onCreateEvent() = onCreate()
        
        @OnLifecycleEvent(Lifecycle.Event.ON_START)
        fun onStartEvent() = onStart()
        
        @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
        fun onResumeEvent() = onResume()
        
        @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
        fun onPauseEvent() = onPause()
        
        @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
        fun onStopEvent() = onStop()
        
        @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
        fun onDestroyEvent() = onDestroy()
    })
}

2. 扩展属性

2.1 基础语法

kotlin
// 扩展属性(只有 getter)
val String.lastChar: Char
    get() = this[this.length - 1]

// 使用
val last = "Kotlin".lastChar  // 'n'

// 扩展属性(getter + setter)
var StringBuilder.lastChar: Char
    get() = this[length - 1]
    set(value) {
        this[length - 1] = value
    }

// 使用
val sb = StringBuilder("Kotln")
sb.lastChar = 'i'  // "Kotlin"

2.2 扩展属性的限制

kotlin
// ❌ 扩展属性不能有 backing field
// var String.name: String  // ❌ 错误:无法存储值

// ✅ 必须提供 getter/setter
var String.name: String
    get() = "default"  // 必须实现 getter
    set(value) {}      // 可选 setter

// ✅ 使用委托存储
class User {
    var name: String by ::nameStorage
    private var nameStorage: String = ""
}

2.3 扩展属性实战

kotlin
// 1. 计算属性
val Int.isEven: Boolean
    get() = this % 2 == 0

val Int.isOdd: Boolean
    get() = this % 2 != 0

// 使用
println(4.isEven)  // true
println(5.isOdd)   // true

// 2. 字符串属性
val String.isEmptyOrBlank: Boolean
    get() = this.isEmpty() || this.isBlank()

val String.firstWord: String
    get() = this.split(" ").firstOrNull() ?: ""

// 使用
println("".isEmptyOrBlank)     // true
println("  ".isEmptyOrBlank)   // true
println("Hello World".firstWord)  // "Hello"

// 3. 集合属性
val <T> List<T>.isNullOrEmpty: Boolean
    get() = this == null || this.isEmpty()

val <T> List<T>.firstOrNullSafe: T?
    get() = this.firstOrNull()

// 4. Android 扩展
val View.isVisible: Boolean
    get() = visibility == View.VISIBLE

val View.isGone: Boolean
    get() = visibility == View.GONE

// 使用
if (button.isVisible) {
    button.gone()
}

// 5. 日期扩展
val Long.toDate: Date
    get() = Date(this)

val Date.toTimestamp: Long
    get() = this.time

// 使用
val now = System.currentTimeMillis().toDate
val timestamp = Date().toTimestamp

2.4 可空接收者的扩展属性

kotlin
// 为可空类型定义扩展属性
val String?.isNullOrEmpty: Boolean
    get() = this == null || this.isEmpty()

val String?.isNullOrBlank: Boolean
    get() = this == null || this.isBlank()

val List<*>?.isNullOrEmpty: Boolean
    get() = this == null || this.isEmpty()

// 使用
val str: String? = null
println(str.isNullOrEmpty)  // true

val list: List<Int>? = emptyList()
println(list.isNullOrEmpty)  // true

3. 扩展函数原理(静态方法)

3.1 编译原理

扩展函数在编译时被转换为静态方法,接收者作为第一个参数:

kotlin
// Kotlin 代码
fun String.addPrefix(prefix: String): String {
    return "$prefix$this"
}

// 编译为 Java(伪代码)
public final class StringUtilKt {
    public static String addPrefix(String $this, String prefix) {
        return prefix + $this;
    }
}

3.2 Java 调用扩展函数

kotlin
// Kotlin 扩展函数
// File: StringUtils.kt
package com.example.utils

fun String.addPrefix(prefix: String): String {
    return "$prefix$this"
}
java
// Java 调用
// 扩展函数编译为静态方法
import com.example.utils.StringUtilsKt;

String result = StringUtilsKt.addPrefix("Kotlin", "Hello ");

3.3 扩展函数的字节码

kotlin
// Kotlin 代码
fun Int.square(): Int {
    return this * this
}

// 反编译后的 Java 代码(简化)
public final class ExtensionsKt {
    public static final int square(int $this) {
        return $this * $this;
    }
}

3.4 扩展函数的性能

kotlin
// 扩展函数 vs 成员函数
// 性能基本相同,因为扩展函数编译为静态方法

// 扩展函数
fun Int.square(): Int = this * this

// 成员函数(需要包装类)
class IntWrapper(val value: Int) {
    fun square(): Int = value * value
}

// 使用
val result1 = 5.square()  // 静态方法调用
val wrapper = IntWrapper(5)
val result2 = wrapper.square()  // 实例方法调用

// 基本类型扩展函数没有装箱开销
// 因为编译为静态方法,接收者是基本类型

3.5 扩展函数的内联优化

kotlin
// 内联扩展函数
inline fun Int.square(): Int = this * this

// 编译后直接内联
val result = 5 * 5  // 没有方法调用开销

// 高阶扩展函数应该使用 inline
inline fun <T> List<T>.forEach(action: (T) -> Unit) {
    for (element in this) action(element)
}

// 内联后
for (element in list) {
    // 直接执行 lambda 内容
}

4. 扩展函数与成员函数优先级

4.1 优先级规则

kotlin
// 规则 1:成员函数优先于扩展函数
class MyClass {
    fun foo(): String = "member"
}

fun MyClass.foo(): String = "extension"

val obj = MyClass()
println(obj.foo())  // "member" - 成员函数优先

// 规则 2:扩展函数是静态解析
class A
fun A.foo() = "extension"

class B {
    fun foo() = "member"
}

val a = A()
val b = B()

println(a.foo())  // "extension"
println(b.foo())  // "member"

4.2 静态解析示例

kotlin
// 扩展函数的解析基于声明时类型,不是运行时类型
open class Animal
class Dog : Animal()

fun Animal.speak() = "Animal sound"
fun Dog.speak() = "Woof"

val animal: Animal = Dog()
println(animal.speak())  // "Animal sound" - 基于声明类型 Animal

val dog: Dog = Dog()
println(dog.speak())  // "Woof" - 基于声明类型 Dog

// 成员函数是动态解析
open class Animal2 {
    open fun speak() = "Animal sound"
}

class Dog2 : Animal2() {
    override fun speak() = "Woof"
}

val animal2: Animal2 = Dog2()
println(animal2.speak())  // "Woof" - 运行时类型 Dog2

4.3 扩展函数遮蔽

kotlin
// 扩展函数可以被同名的扩展函数遮蔽
// 导入顺序决定使用哪个扩展

// File1.kt
fun String.print() {
    println("File1: $this")
}

// File2.kt
fun String.print() {
    println("File2: $this")
}

// File3.kt
import File1.print  // 后导入的覆盖先导入的
import File2.print

fun main() {
    "Hello".print()  // "File2: Hello"
}

4.4 最佳实践

kotlin
// ✅ 推荐:避免与成员函数同名
// 成员函数
class User {
    fun getDisplayName(): String = name
}

// 扩展函数使用不同名称
fun User.formattedName(): String {
    return "User: ${getDisplayName()}"
}

// ✅ 推荐:在文档中说明扩展函数
/**
 * 扩展函数:将 View 设置为 GONE
 * 替代:view.visibility = View.GONE
 */
fun View.gone() {
    visibility = View.GONE
}

// ❌ 避免:与标准库扩展函数同名
// fun String.isEmpty(): Boolean  // ❌ 与标准库冲突

5. 扩展函数作用域

5.1 顶层扩展函数

kotlin
// 文件顶层定义(最常用)
// File: StringExtensions.kt
package com.example.extensions

fun String.addPrefix(prefix: String): String {
    return "$prefix$this"
}

// 使用
import com.example.extensions.addPrefix

val result = "Kotlin".addPrefix("Hello ")

5.2 类内扩展函数

kotlin
// 在类内部定义扩展函数
class StringUtils {
    // 实例扩展函数
    fun String.addPrefix(prefix: String): String {
        return "$prefix$this"
    }
    
    // 伴生对象中的扩展函数
    companion object {
        fun String.addSuffix(suffix: String): String {
            return "$this$suffix"
        }
    }
}

// 使用
val utils = StringUtils()
val result1 = "Kotlin".addPrefix("Hello ")  // 需要实例

val result2 = "Kotlin".addSuffix(" World")  // 伴生对象,可以直接调用

5.3 对象内扩展函数

kotlin
// 在 object 中定义
object Extensions {
    fun String.addPrefix(prefix: String): String {
        return "$prefix$this"
    }
}

// 使用
import Extensions.addPrefix

val result = "Kotlin".addPrefix("Hello ")

5.4 扩展函数作用域限制

kotlin
// 作用域函数内的扩展
class Builder {
    var name: String = ""
    var age: Int = 0
}

fun build(block: Builder.() -> Unit): Builder {
    return Builder().apply(block)
}

// 使用
val person = build {
    name = "张三"  // this 是 Builder
    age = 25
}

// 带接收者的函数类型
val block: Builder.() -> Unit = {
    name = "李四"
    age = 30
}

val person2 = Builder().apply(block)

5.5 扩展函数导入管理

kotlin
// 按需导入
import com.example.extensions.addPrefix
import com.example.extensions.addSuffix

// 通配符导入(不推荐)
import com.example.extensions.*

// 别名解决冲突
import com.example.extensions.addPrefix as addPrefixExt
import com.example.utils.addPrefix as addPrefixUtil

// 使用
val result1 = "Kotlin".addPrefixExt("Hello ")
val result2 = "Kotlin".addPrefixUtil("Hi ")

6. 常见扩展函数示例

6.1 String 扩展

kotlin
// 1. 验证扩展
fun String.isValidEmail(): Boolean {
    return this.contains("@") && this.contains(".")
}

fun String.isValidPhone(): Boolean {
    return this.matches(Regex("^1[3-9]\\d{9}$"))
}

fun String.isValidUrl(): Boolean {
    return this.startsWith("http://") || this.startsWith("https://")
}

// 2. 格式化扩展
fun String.toTitleCase(): String {
    return this.split(" ").joinToString(" ") { word ->
        word.replaceFirstChar { it.uppercase() }
    }
}

fun String.toSnakeCase(): String {
    return this.replace(Regex("([a-z])([A-Z])"), "$1_$2").lowercase()
}

fun String.toCamelCase(): String {
    return this.split("_").joinToString("") { word ->
        word.replaceFirstChar { it.uppercase() }
    }.replaceFirstChar { it.lowercase() }
}

// 3. 截取扩展
fun String.truncate(maxLength: Int, suffix: String = "..."): String {
    return if (this.length <= maxLength) this else this.take(maxLength) + suffix
}

fun String.substringAfterLast(separator: Char, missingDelimiterValue: String = this): String {
    val index = this.lastIndexOf(separator)
    return if (index == -1) missingDelimiterValue else this.substring(index + 1)
}

// 4. HTML 扩展
fun String.escapeHtml(): String {
    return this
        .replace("&", "&amp;")
        .replace("<", "&lt;")
        .replace(">", "&gt;")
        .replace("\"", "&quot;")
        .replace("'", "&#39;")
}

fun String.stripHtml(): String {
    return this.replace(Regex("<[^>]*>"), "")
}

// 使用示例
println("hello world".toTitleCase())  // "Hello World"
println("hello_world".toCamelCase())  // "helloWorld"
println("very long text".truncate(10))  // "very lon..."
println("<script>alert('XSS')</script>".stripHtml())  // "alert('XSS')"

6.2 Collection 扩展

kotlin
// 1. 安全访问
fun <T> List<T>.safeGet(index: Int): T? {
    return if (index in indices) this[index] else null
}

fun <T> List<T>.firstOrNullSafe(predicate: (T) -> Boolean): T? {
    return this.filter(predicate).firstOrNull()
}

// 2. 批量操作
fun <T> List<T>.chunkedSorted(size: Int, comparator: Comparator<T>): List<List<T>> {
    return this.chunked(size).map { it.sortedWith(comparator) }
}

fun <T, R> List<T>.mapNotNullNull(transform: (T) -> R?): List<R> {
    return this.mapNotNull(transform)
}

// 3. 统计扩展
fun List<Int>.averageOrNull(): Double? {
    return if (this.isEmpty()) null else this.average()
}

fun <T> List<T>.countTrue(predicate: (T) -> Boolean): Int {
    return this.count(predicate)
}

// 4. 去重扩展
fun <T> List<T>.distinctByKeys(selector: (T) -> Any): List<T> {
    val seen = mutableSetOf<Any>()
    return this.filter { seen.add(selector(it)) }
}

// 使用示例
val list = listOf(1, 2, 3, 4, 5)
println(list.safeGet(10))  // null
println(list.averageOrNull())  // 3.0

val users = listOf(
    User("A", 20),
    User("B", 20),
    User("C", 30)
)
val distinctByAge = users.distinctByKeys { it.age }

6.3 Android 专用扩展

kotlin
// 1. View 扩展
fun View.setVisible(visible: Boolean) {
    visibility = if (visible) View.VISIBLE else View.GONE
}

fun View.enableWithAlpha(enabled: Boolean) {
    isEnabled = enabled
    alpha = if (enabled) 1.0f else 0.5f
}

fun View.doOnPreDraw(action: (View) -> Unit) {
    viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
        override fun onPreDraw(): Boolean {
            viewTreeObserver.removeOnPreDrawListener(this)
            action(this@doOnPreDraw)
            return true
        }
    })
}

fun View.animateScale(
    scale: Float,
    duration: Long = 200,
    interpolator: Interpolator = AccelerateDecelerateInterpolator()
) {
    animate()
        .scaleX(scale)
        .scaleY(scale)
        .setDuration(duration)
        .setInterpolator(interpolator)
        .start()
}

// 2. TextView 扩展
fun TextView.setHtml(text: String) {
    this.text = Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY)
}

fun TextView.setDrawableLeft(@DrawableRes resId: Int) {
    val drawable = ContextCompat.getDrawable(context, resId)
    drawable?.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
    setCompoundDrawables(drawable, null, null, null)
}

fun TextView.addClickSpan(
    text: String,
    clickableText: String,
    onClick: () -> Unit
) {
    val spannable = SpannableString(text)
    val startIndex = text.indexOf(clickableText)
    if (startIndex != -1) {
        val endIndex = startIndex + clickableText.length
        spannable.setSpan(
            object : ClickableSpan() {
                override fun onClick(widget: View) {
                    onClick()
                }
                
                override fun updateDrawState(ds: TextPaint) {
                    super.updateDrawState(ds)
                    ds.isUnderlineText = true
                    ds.color = ContextCompat.getColor(context, R.color.colorPrimary)
                }
            },
            startIndex,
            endIndex,
            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )
    }
    this.text = spannable
    movementMethod = LinkMovementMethod.getInstance()
}

// 3. Fragment 扩展
inline fun Fragment.viewBinding(
    crossinline bindingProvider: (View) -> ViewBinding
): Lazy<ViewBinding> {
    return lazy {
        checkNotNull(view) { "Fragment's view is null" }
        bindingProvider(view!!)
    }
}

fun Fragment.backPressed(action: () -> Boolean) {
    requireActivity().onBackPressedDispatcher.addCallback(
        viewLifecycleOwner,
        object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                if (!action()) {
                    isEnabled = false
                    requireActivity().onBackPressed()
                }
            }
        }
    )
}

// 4. Context 扩展
fun Context.getScreenWidth(): Int {
    return resources.displayMetrics.widthPixels
}

fun Context.getScreenHeight(): Int {
    return resources.displayMetrics.heightPixels
}

fun Context.getStatusBarHeight(): Int {
    val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
    return if (resourceId > 0) resources.getDimensionPixelSize(resourceId) else 0
}

fun Context.isNetworkAvailable(): Boolean {
    val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    val networkInfo = connectivityManager.activeNetworkInfo
    return networkInfo != null && networkInfo.isConnected
}

// 5. Lifecycle 扩展
fun LifecycleOwner.onCreated(action: () -> Unit) {
    lifecycle.addObserver(object : LifecycleObserver {
        @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
        fun onCreate() {
            action()
            lifecycle.removeObserver(this)
        }
    })
}

fun LifecycleOwner.onStarted(action: () -> Unit) {
    lifecycle.addObserver(object : LifecycleObserver {
        @OnLifecycleEvent(Lifecycle.Event.ON_START)
        fun onStart() {
            action()
            lifecycle.removeObserver(this)
        }
    })
}

6.4 协程扩展

kotlin
// 1. Flow 扩展
fun <T> Flow<T>.debounceIf(condition: () -> Boolean, timeoutMillis: Long): Flow<T> {
    return if (condition()) debounce(timeoutMillis) else this
}

fun <T> Flow<T>.retryWithDelay(
    maxRetries: Int = 3,
    delayMillis: Long = 1000,
    shouldRetry: (Throwable) -> Boolean = { true }
): Flow<T> {
    return retry(maxRetries) { e ->
        shouldRetry(e).also { if (it) delay(delayMillis) }
    }
}

// 2. suspend 扩展
suspend fun <T> Deferred<T>.awaitOrNull(): T? {
    return try {
        await()
    } catch (e: Exception) {
        null
    }
}

suspend fun Job.cancelAndJoin() {
    cancel()
    join()
}

// 3. 作用域扩展
fun CoroutineScope.launchSafe(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    return launch(context, start) {
        try {
            block()
        } catch (e: Exception) {
            // 处理异常
        }
    }
}

6.5 实用工具扩展

kotlin
// 1. 时间扩展
fun Long.toSeconds(): Long = this / 1000
fun Long.toMinutes(): Long = this / 60000
fun Long.toHours(): Long = this / 3600000

fun Long.formatDuration(): String {
    val hours = this.toHours()
    val minutes = (this.toMinutes() % 60)
    val seconds = (this.toSeconds() % 60)
    return String.format("%02d:%02d:%02d", hours, minutes, seconds)
}

// 2. 数字扩展
fun Int.toPercentage(total: Int): String {
    return if (total == 0) "0%" else "${(this * 100 / total)}%"
}

fun Double.roundTo(decimalPlaces: Int): Double {
    val factor = Math.pow(10.0, decimalPlaces.toDouble())
    return Math.round(this * factor) / factor
}

fun Int.inRange(range: IntRange): Boolean = this in range

// 3. Any 扩展
fun Any?.toJson(): String {
    return Gson().toJson(this)
}

inline fun <reified T> String.fromJson(): T? {
    return try {
        Gson().fromJson(this, T::class.java)
    } catch (e: Exception) {
        null
    }
}

// 4. Boolean 扩展
fun Boolean.yesNo(): String = if (this) "Yes" else "No"
fun Boolean.trueFalse(): String = if (this) "true" else "false"
fun Boolean.toInt(): Int = if (this) 1 else 0

7. 面试考点汇总

7.1 基础问题

Q1: 什么是扩展函数?如何定义?

kotlin
// 答案要点:
// 1. 扩展函数允许为现有类添加新方法
// 2. 无需继承或修改原类
// 3. 语法:接收者类型。函数名
// 4. 在函数体内通过 this 访问接收者

// 定义
fun String.addPrefix(prefix: String): String {
    return "$prefix$this"
}

// 使用
val result = "Kotlin".addPrefix("Hello ")

Q2: 扩展函数可以访问私有成员吗?

kotlin
// 答案要点:
// 1. 不能访问私有成员
// 2. 只能访问 public/protected 成员
// 3. 扩展函数是静态方法,不是真正的成员

class MyClass {
    private val secret = "hidden"
}

// ❌ 错误:无法访问 private 成员
// fun MyClass.getSecret() = secret

Q3: 什么是扩展属性?有什么限制?

kotlin
// 答案要点:
// 1. 扩展属性类似扩展函数,但是属性形式
// 2. 不能有 backing field
// 3. 必须提供 getter(setter 可选)
// 4. 通常用于计算属性

// 定义
val String.lastChar: Char
    get() = this[this.length - 1]

// ❌ 错误:无法存储值
// var String.name: String

7.2 进阶问题

Q4: 扩展函数和成员函数的优先级?

kotlin
// 答案要点:
// 1. 成员函数优先于扩展函数
// 2. 扩展函数是静态解析(基于声明类型)
// 3. 成员函数是动态解析(基于运行时类型)

class MyClass {
    fun foo() = "member"
}

fun MyClass.foo() = "extension"

val obj = MyClass()
println(obj.foo())  // "member" - 成员函数优先

Q5: 扩展函数的编译原理?

kotlin
// 答案要点:
// 1. 编译为静态方法
// 2. 接收者作为第一个参数
// 3. 文件名 + Kt 作为类名
// 4. 没有额外的对象创建开销

// Kotlin
fun Int.square(): Int = this * this

// 编译为 Java
public class ExtensionsKt {
    public static int square(int $this) {
        return $this * $this;
    }
}

Q6: 可空接收者的扩展函数如何使用?

kotlin
// 答案要点:
// 1. 为可空类型定义扩展函数
// 2. 函数体内需要处理 null 情况
// 3. 可以安全调用

fun String?.isNullOrEmpty(): Boolean {
    return this == null || this.isEmpty()
}

// 使用
val str: String? = null
println(str.isNullOrEmpty())  // true

7.3 原理问题

Q7: 为什么扩展函数是静态解析?

kotlin
// 答案要点:
// 1. 扩展函数编译为静态方法
// 2. 静态方法在编译时确定
// 3. 无法在运行时动态分发
// 4. 与成员函数的虚方法调用不同

open class Animal
class Dog : Animal()

fun Animal.speak() = "Animal"
fun Dog.speak() = "Woof"

val animal: Animal = Dog()
println(animal.speak())  // "Animal" - 基于声明类型

Q8: 扩展函数的性能如何?

kotlin
// 答案要点:
// 1. 基本类型扩展函数没有装箱开销
// 2. 编译为静态方法,调用开销小
// 3. 可以使用 inline 进一步优化
// 4. 与工具类静态方法性能相当

// 基本类型扩展(无装箱)
fun Int.square(): Int = this * this

// 内联优化
inline fun <T> List<T>.forEach(action: (T) -> Unit) {
    for (element in this) action(element)
}

7.4 实战问题

Q9: 如何组织扩展函数代码?

kotlin
// 答案要点:
// 1. 按接收者类型分组文件
// 2. 使用清晰的包名
// 3. 添加文档说明
// 4. 避免与标准库冲突

// 文件结构
// - StringExtensions.kt
// - ViewExtensions.kt
// - ContextExtensions.kt
// - CollectionExtensions.kt

// 包名
package com.example.extensions.string
package com.example.extensions.android

Q10: 扩展函数在 Android 中的最佳实践?

kotlin
// 答案要点:
// 1. View 扩展简化 UI 操作
// 2. Context 扩展提供便捷方法
// 3. Lifecycle 扩展管理生命周期
// 4. 协程扩展简化异步操作
// 5. 避免过度使用导致代码分散

// View 扩展
fun View.visible() { visibility = View.VISIBLE }
fun View.gone() { visibility = View.GONE }

// Context 扩展
fun Context.showToast(message: String) {
    Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}

// Lifecycle 扩展
fun LifecycleOwner.onCreated(action: () -> Unit) {
    lifecycle.addObserver(object : LifecycleObserver {
        @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
        fun onCreate() {
            action()
            lifecycle.removeObserver(this)
        }
    })
}

Q11: 如何解决扩展函数命名冲突?

kotlin
// 答案要点:
// 1. 使用别名导入
// 2. 限定调用(使用类名)
// 3. 避免通配符导入
// 4. 统一团队规范

// 别名导入
import com.example.extensions.addPrefix as addPrefixExt
import com.example.utils.addPrefix as addPrefixUtil

// 使用
val result1 = "Kotlin".addPrefixExt("Hello ")
val result2 = "Kotlin".addPrefixUtil("Hi ")

// 或者使用伴生对象
object Extensions {
    fun String.addPrefix(prefix: String): String {
        return "$prefix$this"
    }
}

val result = "Kotlin".Extensions.addPrefix("Hello ")

最佳实践总结

✅ 推荐做法

kotlin
// 1. 为常用操作创建扩展函数
fun View.visible() { visibility = View.VISIBLE }

// 2. 使用清晰的命名
fun String.isValidEmail(): Boolean

// 3. 添加文档说明
/**
 * 将 View 设置为 GONE
 */
fun View.gone()

// 4. 按类型分组文件
// StringExtensions.kt, ViewExtensions.kt

// 5. 使用内联优化高阶扩展
inline fun <T> List<T>.forEach(action: (T) -> Unit)

❌ 避免做法

kotlin
// 1. 避免与成员函数同名
// fun String.isEmpty()  // ❌ 与标准库冲突

// 2. 避免过度使用
// 每个简单操作都创建扩展函数会导致代码分散

// 3. 避免访问私有成员
// 扩展函数无法访问私有成员

// 4. 避免通配符导入
// import com.example.extensions.*  // ❌

参考资料


最后更新:2026-04-14