Kotlin 委托与扩展函数——新手入门

一、委托(Delegation)

1. 类委托

类委托允许将接口的实现委托给另一个对象,避免继承的局限性,适用于组合模式。

示例代码

kotlin 复制代码
interface Printer {
    fun printMessage(message: String)
}

class ConsolePrinter : Printer {
    override fun printMessage(message: String) {
        println("控制台输出: $message")
    }
}

// 通过委托将Printer接口的实现交给ConsolePrinter
class LogPrinter(printer: Printer) : Printer by printer {
    override fun printMessage(message: String) {
        println("日志记录开始")
        printer.printMessage(message)
        println("日志记录结束")
    }
}

fun main() {
    val consolePrinter = ConsolePrinter()
    val logPrinter = LogPrinter(consolePrinter)
    logPrinter.printMessage("测试消息")
}

使用场景

  • 需要增强或修改现有类的行为(装饰器模式)。
  • 避免多层继承带来的复杂性。

2. 属性委托

属性委托将属性的 getter/setter 逻辑委托给其他对象,Kotlin 标准库提供多种内置委托。

常用内置委托

  • lazy:延迟初始化属性。
  • observable:监听属性变化。
  • vetoable:在赋值前验证值。

示例代码

kotlin 复制代码
import kotlin.properties.Delegates

class Example {
    // 延迟初始化,首次访问时计算
    val lazyValue: String by lazy {
        println("计算lazyValue")
        "Hello, Lazy!"
    }

    // 监听属性变化
    var name: String by Delegates.observable("<未命名>") { _, old, new ->
        println("名称从 $old 更改为 $new")
    }

    // 仅允许非负数值
    var age: Int by Delegates.vetoable(0) { _, _, new ->
        new >= 0
    }
}

fun main() {
    val example = Example()
    println(example.lazyValue) // 输出:计算lazyValue \n Hello, Lazy!
    example.name = "Kotlin"    // 输出:名称从 <未命名> 更改为 Kotlin
    example.age = -5          // 赋值失败,age保持0
    println(example.age)       // 输出:0
}

使用场景

  • lazy:初始化成本高的资源(如数据库连接)。
  • observable:实现数据绑定或响应式UI更新。
  • vetoable:表单输入验证。

3. 自定义属性委托

通过实现 ReadWritePropertyReadOnlyProperty 接口创建自定义委托。

示例代码

kotlin 复制代码
import kotlin.reflect.KProperty

class FormatDelegate(private val format: String) : ReadWriteProperty<Any?, String> {
    private var value: String = ""

    override fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return value.format(format)
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        this.value = value
    }
}

class User {
    var username: String by FormatDelegate("用户名:%s")
}

fun main() {
    val user = User()
    user.username = "Alice"
    println(user.username) // 输出:用户名:Alice
}

使用场景

  • 统一格式化字符串、数值等属性。
  • 自动加密/解密敏感数据字段。

二、扩展函数(Extension Functions)

1. 基本用法

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

示例代码

kotlin 复制代码
// 为String添加判断是否为有效邮件的扩展函数
fun String.isValidEmail(): Boolean {
    return matches(Regex("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}"))
}

// 为Int添加计算阶乘的扩展函数
fun Int.factorial(): Long {
    return if (this <= 1) 1 else this * (this - 1).factorial()
}

fun main() {
    println("[email protected]".isValidEmail()) // 输出:true
    println(5.factorial())                     // 输出:120
}

使用场景

  • 为第三方库或系统类添加实用方法。
  • 增强代码可读性,如 list.quickSort()

2. 扩展属性

类似扩展函数,可以为类添加"属性"(实际是计算属性,无幕后字段)。

示例代码

kotin 复制代码
val String.firstLetter: Char
    get() = if (isEmpty()) ' ' else this[0]

fun main() {
    println("Kotlin".firstLetter) // 输出:K
}

使用场景

  • 提供基于现有属性的快捷访问方式,如 file.extension

3. 扩展与成员冲突

当扩展函数与类成员函数同名时,成员函数优先。

示例代码

kotlin 复制代码
class Example {
    fun print() = println("成员函数")
}

fun Example.print() = println("扩展函数")

fun main() {
    Example().print() // 输出:成员函数
}

三、注意事项

1. 委托的可见性:

  • 委托对象需在类构造时初始化。
  • 避免在委托中持有外部类的引用导致内存泄漏。

2. 扩展的限制:

  • 无法访问类的私有成员。
  • 扩展是静态解析的,不具备多态性。

3. 性能考量:

  • lazy 委托默认是线程安全的,若不需要同步可用 lazy(LazyThreadSafetyMode.NONE) 提升性能。

四、总结

特性 适用场景 优势
类委托 组合优于继承、装饰器模式 减少重复代码,提升灵活性
属性委托 懒加载、属性监听、验证逻辑 逻辑复用,代码简洁
扩展函数 增强现有类功能、第三方库适配 无侵入式扩展,提高可读性
扩展属性 提供快捷访问属性 简化复杂计算逻辑的调用

合理运用委托和扩展函数,能显著提升Kotlin代码的简洁性和可维护性,但需注意避免滥用导致结构混乱。

更多分享

  1. 一文吃透Kotlin中冷流(Clod Flow)和热流(Hot Flow)
  2. 一文带你吃透Kotlin协程的launch()和async()的区别
  3. 一文带你吃透接口(Interface)结合 @AutoService 与 ServiceLoader 详解
  4. Kotlin 作用域函数(let、run、with、apply、also)的使用指南
  5. 一文带你吃透Kotlin中 lateinit 和 by lazy 的区别和用法
相关推荐
Cachel wood18 分钟前
大数据开发知识1:数据仓库
android·大数据·数据仓库·sql·mysql·算法·ab测试
灰色人生qwer28 分钟前
idea中运行groovy程序报错
android·java·intellij-idea
Coffeeee1 小时前
面试官:说几个同步拿到异步操作结果的方式
android·面试·kotlin
小鱼人爱编程2 小时前
离职当晚,我删除了所有同事的微信
android·前端·后端
CYRUS_STUDIO2 小时前
Android 下的 ClassLoader 与 双亲委派机制
android·jvm·逆向
wangz762 小时前
kotlin,编码、解码
kotlin
小墙程序员2 小时前
Linux 基础知识点汇总
android
_一条咸鱼_2 小时前
Android ARouter 跨进程模块深度剖析(五)
android·面试·android jetpack
恋猫de小郭3 小时前
AI 傻傻分不清楚?那么多 AI 变体究竟怎么选?这里快速简单理清!
android·前端·ai编程
叹无痕4 小时前
【adb】bat批处理+adb 自动亮屏,自动解锁屏幕,启动王者荣耀
android·adb·bat