扩展函数和属性是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 = 5
println(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)) // 输出: Banana
println(list.getSafe(5)) // 输出: null
val 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? = null
println(nullString.orEmpty().length) // 输出: 0
val numString: String? = "42"
println(numString.toIntOrZero()) // 输出: 42
println(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: Int
get() = split("\\s+".toRegex()).filter { it.isNotBlank() }.size
// 为Int添加扩展属性:是否为偶数
val Int.isEven: Boolean
get() = this % 2 == 0
fun main() {
val text = "Kotlin extensions are powerful"
println("单词数: ${text.wordCount}") // 输出: 4
println("10是偶数吗? ${10.isEven}") // 输出: true
println("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 = 0L
for (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)