Appearance
Kotlin 扩展函数与扩展属性详解 🔧
Android 面试必考 Kotlin 扩展函数,包含扩展函数原理、扩展属性、作用域、优先级等核心知识点
目录
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 // 01.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().toTimestamp2.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) // true3. 扩展函数原理(静态方法)
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" - 运行时类型 Dog24.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("&", "&")
.replace("<", "<")
.replace(">", ">")
.replace("\"", """)
.replace("'", "'")
}
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 07. 面试考点汇总
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() = secretQ3: 什么是扩展属性?有什么限制?
kotlin
// 答案要点:
// 1. 扩展属性类似扩展函数,但是属性形式
// 2. 不能有 backing field
// 3. 必须提供 getter(setter 可选)
// 4. 通常用于计算属性
// 定义
val String.lastChar: Char
get() = this[this.length - 1]
// ❌ 错误:无法存储值
// var String.name: String7.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()) // true7.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.androidQ10: 扩展函数在 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