扩展函数和属性是Kotlin中最具特色的功能之一,它们允许我们在不修改原有类的情况下为类添加新功能。这种设计既保持了类的封装性,又提供了强大的扩展能力。
一、扩展函数:为现有类添加新行为
1.1 基础扩展函数
扩展函数允许我们为任何类(包括Java类)添加新方法:
// 为String类添加扩展函数
fun String.addExclamation(): String {return "$this!"
}// 为Int添加平方计算
fun Int.square(): Int {return this * this
}fun main() {val greeting = "Hello"println(greeting.addExclamation()) // 输出: Hello!val number = 5println(number.square()) // 输出: 25
}
1.2 实用扩展函数示例
1.2.1 集合操作扩展
// 安全获取元素,越界时返回null
fun <T> List<T>.getSafe(index: Int): T? {return if (index in indices) get(index) else null
}// 统计满足条件的元素数量
fun <T> Iterable<T>.countBy(predicate: (T) -> Boolean): Int {return count(predicate)
}fun main() {val list = listOf("Apple", "Banana", "Cherry")println(list.getSafe(1)) // 输出: Bananaprintln(list.getSafe(5)) // 输出: nullval count = list.countBy { it.length > 5 }println("长度>5的单词数: $count") // 输出: 2
}
1.2.2 日期时间处理扩展
// 计算两个日期之间的天数
fun LocalDate.daysUntil(other: LocalDate): Long {return ChronoUnit.DAYS.between(this, other)
}// 判断是否是周末
fun LocalDate.isWeekend(): Boolean {return dayOfWeek.value in listOf(6, 7) // 6=周六, 7=周日
}fun main() {val today = LocalDate.now()val futureDate = today.plusDays(10)println("距离未来日期还有 ${today.daysUntil(futureDate)} 天")println("今天是周末吗? ${today.isWeekend()}")
}
1.3 扩展函数的高级用法
1.3.1 可空接收者扩展
// 为可空String添加扩展函数
fun String?.orEmpty(): String {return this ?: ""
}// 安全解析Int
fun String?.toIntOrZero(): Int {return this?.toIntOrNull() ?: 0
}fun main() {val nullString: String? = nullprintln(nullString.orEmpty().length) // 输出: 0val numString: String? = "42"println(numString.toIntOrZero()) // 输出: 42println(nullString.toIntOrZero()) // 输出: 0
}
1.3.2 泛型扩展函数
// 交换Map中的键值对
fun <K, V> Map<K, V>.invert(): Map<V, K> {return map { Pair(it.value, it.key) }.toMap()
}// 安全转换集合类型
fun <T, R> Collection<T>.convertAll(transform: (T) -> R): List<R> {return mapNotNull { runCatching { transform(it) }.getOrNull() }
}fun main() {val originalMap = mapOf("A" to 1, "B" to 2)println(originalMap.invert()) // 输出: {1=A, 2=B}val strings = listOf("1", "2", "three", "4")val numbers = strings.convertAll { it.toInt() }println(numbers) // 输出: [1, 2, 4]
}
二、扩展属性:为类添加新属性
扩展属性允许我们为现有类添加计算属性:
2.1 基础扩展属性
// 为String添加扩展属性:单词数
val String.wordCount: Intget() = split("\\s+".toRegex()).filter { it.isNotBlank() }.size// 为Int添加扩展属性:是否为偶数
val Int.isEven: Booleanget() = this % 2 == 0fun main() {val text = "Kotlin extensions are powerful"println("单词数: ${text.wordCount}") // 输出: 4println("10是偶数吗? ${10.isEven}") // 输出: trueprintln("7是偶数吗? ${7.isEven}") // 输出: false
}
三、实战案例:构建DSL风格的验证库
// 验证结果类
data class ValidationResult(val isValid: Boolean,val errors: List<String> = emptyList()
) {operator fun plus(other: ValidationResult): ValidationResult {return ValidationResult(isValid = isValid && other.isValid,errors = errors + other.errors)}
}// 验证器扩展函数
fun String.validate(block: StringValidator.() -> Unit): ValidationResult {val validator = StringValidator(this)validator.block()return validator.result
}// 验证器类
class StringValidator(private val value: String) {private var result = ValidationResult(true)fun notEmpty(message: String = "不能为空") {if (value.isBlank()) {result = ValidationResult(false, result.errors + message)}}fun minLength(length: Int, message: String = "长度不足") {if (value.length < length) {result = ValidationResult(false, result.errors + "$message (最小$length)")}}fun maxLength(length: Int, message: String = "长度超限") {if (value.length > length) {result = ValidationResult(false, result.errors + "$message (最大$length)")}}fun matches(regex: Regex, message: String = "格式不匹配") {if (!value.matches(regex)) {result = ValidationResult(false, result.errors + message)}}
}// 使用示例
fun main() {val password = "secret123"val validation = password.validate {notEmpty()minLength(8)maxLength(20)matches(Regex(".*[0-9].*"), "必须包含数字")matches(Regex(".*[A-Z].*"), "必须包含大写字母") // 这个会失败}if (validation.isValid) {println("密码有效")} else {println("密码无效,原因:")validation.errors.forEach { println("- $it") }}/* 输出:密码无效,原因:- 必须包含大写字母*/
}
四、扩展函数与属性的最佳实践
4.1 设计原则
单一职责:每个扩展函数只做一件事
命名清晰:函数名应准确表达其功能
避免滥用:不是所有工具函数都适合做成扩展
文档完善:为非直观的扩展添加KDoc
4.2 实用技巧
4.2.1 链式调用设计
// 链式构建器风格
class StringBuilder {private val builder = StringBuilder()fun append(text: String): StringBuilder {builder.append(text)return this}fun appendLine(text: String = ""): StringBuilder {builder.appendLine(text)return this}override fun toString(): String = builder.toString()
}// 扩展函数支持链式调用
fun StringBuilder.bold(): StringBuilder {return append("<b>").append(this.toString()).append("</b>")
}fun main() {val result = StringBuilder().append("Hello").appendLine(", Kotlin!").appendLine("This is bold:").bold()println(result)
}
4.2 性能考虑
扩展函数是静态解析的,没有运行时开销
对于频繁调用的扩展,考虑内联(inline)优化
扩展属性有轻微性能开销(每次访问都会调用getter)
// 内联扩展函数示例
inline fun <T> Iterable<T>.sumByLong(selector: (T) -> Long): Long {var sum = 0Lfor (element in this) {sum += selector(element)}return sum
}
五、总结与进阶方向
5.1 核心要点回顾
扩展函数和属性是静态解析的,不会影响原有类的实例
扩展函数可以定义在顶层、类内部或对象中
扩展属性不能有幕后字段(backing field)
可空接收者扩展可以安全处理null值
5.2 扩展函数 vs 成员函数
5.3 进阶学习方向
探索Kotlin标准库中的常用扩展(如also, apply, let等)
研究扩展函数在Android开发中的应用(如View绑定)
学习如何为第三方库编写扩展函数
实践用扩展函数构建领域特定语言(DSL)