Kotlin的委托是什么?在看源码的时候不知道他的作用是什么,为什么使用,那么你看看这篇文章。

目录

  1. 什么是委托?

  2. 我们先来看看委托的基础语法

    2.1 类委托:让一个类"代理"另一个类的功能

    2.2 属性委托:延迟初始化

  3. 总结一下委托的好处

  4. 那么在代码中如何使用?应用场景。

    4.1 LazyThreadSafetyMode.NONE


一、什么是委托?

想象你要出国旅游,但不会说英语,于是你找了个翻译帮你沟通。翻译就是你的"委托对象",你"委托"翻译帮你处理语言问题。

在 Kotlin 中,委托(Delegation) 就是这个道理:让一个对象(委托对象)代替另一个对象(被委托对象)完成某些任务。

用代码来举例,就是:

kotlin 复制代码
// 定义接口
interface Player {
    fun play()
    fun stop()
}

// 普通播放器实现接口
class NormalPlayer : Player {
    override fun play() { println("播放音乐") }
    override fun stop() { println("停止播放") }
}

// 增强播放器,通过委托复用 NormalPlayer 的代码
class EnhancedPlayer(private val player: Player) : Player by player {
    // 只重写需要增强的方法
    override fun play() {
        println("增强音效!")
        player.play() // 调用委托对象的 play()
    }
}

fun main() {
    val normalPlayer = NormalPlayer()
    val enhancedPlayer = EnhancedPlayer(normalPlayer)
    
    enhancedPlayer.play() // 输出:增强音效! → 播放音乐
    enhancedPlayer.stop() // 输出:停止播放(直接调用委托对象的方法)
}

EnhancedPlayer 通过 by player 将接口方法委托给 player 对象。 只重写需要增强的方法(如 play()),其他方法(如 stop())自动调用委托对象的方法

如果学过装饰者设计模式,会发现有一举同工之妙。


二、我们先来看看委托的基础语法

2.1 类委托:让一个类"代理"另一个类的功能

其实就是上面的播放器代码,下面代码就省略掉。 场景:比如你有一个播放器接口,想让一个普通播放器和增强播放器都实现这个接口,但不想重复写代码。

2.2 属性委托:

场景:比如你想实现一个属性的延迟加载(第一次访问时才初始化)。

kotlin 复制代码
// 延迟初始化:第一次访问时才计算值
    val bio: String by lazy {
        println("计算 bio...")
        "我是程序员,喜欢 Kotlin!"
    }

fun main() {
    val user = User()
    
    // 1. 访问 bio(第一次触发计算)
    println(user.bio) // 输出:计算 bio... → 我是程序员,喜欢 Kotlin!
    println(user.bio) // 直接输出:我是程序员...(不再计算)
}

lazy:用于延迟初始化,常用于资源密集型操作(如读取文件、数据库连接)。

属性委托,委托给谁呢?属性委托实际上是将属性的 ​getter 和 ​setter 的访问逻辑委托给另一个对象​(即委托对象)

这里 lazy { ... } 返回的是一个 Lazy 对象,它内部实现了 getValue 方法。当访问 myValue 时,实际会调用 Lazy 对象的 getValue 方法,从而实现属性的延迟初始化逻辑


三、总结一下委托的好处

  1. 避免重复写代码:通过上面的代码,我们也知道,多个类需要相同功能时,将类传递进来,交给委托对象统一处理。 可以省掉了很多代码。
  2. 代替继承:继承是"父子关系",不够灵活;委托是"合作关系",更灵活。
  3. 解耦代码:把功能拆分到不同的委托对象中,让代码更干净。
  4. 动态切换功能:运行时可以换不同的委托对象,实现不同行为。
  5. 结合lazy实现延迟初始化:让资源得到更好、安全的利用。

四、那么在代码中如何使用?应用场景

我们在看源码的时候,会发现有很多使用了委托的地方。

kotlin 复制代码
private val mToastHandler by lazy { 
    Handler(Looper.getMainLooper()) 
}

这里的意思就是,我使用mToastHandler的时候,才会赋值 Handler(Looper.getMainLooper()) 过来 。

但我们在来看看这个。

kotlin 复制代码
//父类
abstract class BaseFrameDialog<VB : ViewBinding> (
    protected var mContext: Context,
) :
    Dialog(mContext, R.style.base_DialogBgD),FrameView<VB> {

    protected val mBinding: VB by lazy(mode = LazyThreadSafetyMode.NONE) { createVB() }
}

//子类实现

/**
 * 开关机的对话框
 */
class DevOnOffDialog(mContext: Context) : BaseFrameDialog<BackstageDialogDevonoffBinding>(mContext) { 
    override fun BackstageDialogDevonoffBinding.initView() {
        tvBack.setOnClickListener {
            dismiss()
        }
    }
    override fun createVB() = BackstageDialogDevonoffBinding.inflate(layoutInflater)
}

protected val mBinding: VB by lazy(mode = LazyThreadSafetyMode.NONE) { createVB() }为什么mBinding需要延迟初始化呢?一般这个属性不是一定会使用的?还需要延迟初始化节省资源是为了什么??

其实延迟初始化,不仅仅是为节省资源,还有一个是因为生命周期、时间顺序的限制。

在 Android 的 Activity 或 Fragment 中,视图绑定(View Binding)或数据绑定(Data Binding)的初始化必须在 生命周期回调之后(例如 onCreate() 或 onCreateView()),否则会引发崩溃。所以,我们需要等到他用的时候才初始化,这样之前肯定就准备好了


4.1 我们会注意有一个mode = LazyThreadSafetyMode.NONE

LazyThreadSafetyMode.NONE 表示:

  1. 不保证线程安全,初始化代码可能在多线程环境下 被多次执行。
  2. 性能更高,因为跳过了同步锁检查。

那么这里为什么敢使用NONE呢?

因为mBinding 的初始化必然发生在主线程:BaseFrameDialog 的构造函数在创建对话框时调用,而对话框的实例化通常发生在主线程(例如在 Activity 的 onCreate 中)。因此,createVB() 方法生成 ViewBinding 的代码也必然在主线程执行。

由于所有对 mBinding 的访问都被限制在主线程,​不存在多线程竞争初始化的问题,因此无需线程安全机制。

相关推荐
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone2 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc2 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo2 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077002 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
ai2work2 天前
ch23 综合复刻:从零做一个最小可用版本(capstone)
kotlin
其实防守也摸鱼2 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
ai2work2 天前
ch21 签名、校验与发版
kotlin
AFinalStone2 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui