Kotlin - 属性代理

简介: Kotlin - 属性代理

本系列学习教程笔记属于详细讲解Kotlin语法的教程,需要快速学习Kotlin语法的小伙伴可以查看“简洁” 系列的教程

快速入门请阅读如下简洁教程:
Kotlin学习教程(一)
Kotlin学习教程(二)
Kotlin学习教程(三)
Kotlin学习教程(四)
Kotlin学习教程(五)
Kotlin学习教程(六)
Kotlin学习教程(七)
Kotlin学习教程(八)
Kotlin学习教程(九)
Kotlin学习教程(十)

Kotlin教程笔记(17) - 属性代理

imgKotlin - 属性代理

#自定义属性代理

代理也叫委托,Kotlin 支持属性代理,把一个属性的获取与赋值交给一个“中介”(下称 Delegate)去管理,属性代理的语法是: val/var <属性名>: <类型> by <表达式> ,by 后面是 Delegate 对象,被代理属性的 get()set() 会给 Delegate 对象对应的 getValue()setValue() 分别代理,因此 Delegete 可以这么写:

注意:Delegate 并不需要实现任何接口,仅需提供 getValue()setValue() 即可。

class Delegate {
    private var _value: String? = null

    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        println("getValue() thisRef=$thisRef, property=${property.name}")
        return _value ?: ""
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("setValue() thisRef=$thisRef, property=${property.name}, value=$value")
        _value = value
    }
}

该 Delegate 的 getValue()setValue() 方法对自己的 _value 属性进行获取与赋值,并分别在这 2 个方法中打印输出各参数值,下面我们将一个类成员属性交给 Delegate 对象代理:

class Animal {
    var name: String by Delegate()
}
fun main() {
    val animal = Animal()
    animal.name = "旺财" // setValue() thisRef=com.charylin.kotlinlearn.Animal@1b28cdfa, property=name, value=旺财
    println(animal.name) // getValue() thisRef=com.charylin.kotlinlearn.Animal@1b28cdfa, property=name
                       // 旺财
}

当代理属性被赋值与访问时,就会输出 Delegate 对象 setValue()getValue() 中的日志,从输出的日志可以看出,thisRef 是代理属性的实例对象,property 是代理属性的包装。可见,属性代理相比单纯的属性操作更加强大,在某些场景下,属性值的访问会比较复杂,如文件或 Redis,属性代理可以将文件或 Redis 的操作全部交给 Delegate 完成,而业务代码不需要知道具体实现,这对代码解耦与简化很有帮助。

#延迟属性 lazy

Kotlin 官方提供了 lazy 代理,可以延迟初始化 val 常量,且只会初始化一次:

class Animal {
    val age: Int by lazy {
        println("设置age")
        18 // lambda表达式会把最后语句的执行结果作为返回值
    }
}
fun main() {
    val animal = Animal()
    println(animal.age) // 设置age
                        // 18
    println(animal.age) // 18
}

可以看到 println("设置age") 只被输出了一次,这是怎么办到的呢?按住 ctrl +鼠标左键,点击 lazy 查看源代码:

// LazyJVM.kt
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)

lazy() 函数需要传入一个 lambda 表达式,返回值是 Lazy<T>,返回结果 SynchronizedLazyImpl(initializer) 就是 Lazy<T> 的实现类对象,我们知道 by 后面接的是 Delegate 对象,lazy() 函数返回的 SynchronizedLazyImpl 就是真正的 Delegate ,再来看看 LazySynchronizedLazyImpl 的源代码:

// Lazy.kt
public interface Lazy<out T> {
    public val value: T
    ...
}

// LazyJVM.kt
private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
    private var initializer: (() -> T)? = initializer
    @Volatile private var _value: Any? = UNINITIALIZED_VALUE
    ...

    override val value: T
        get() {
            val _v1 = _value
            if (_v1 !== UNINITIALIZED_VALUE) {
                // 已经初始化过了,直接返回
                return _v1 as T
            }

            return synchronized(lock) {
                ...
                // 执行initializer()初始化_value值
                val typedValue = initializer!!()
                _value = typedValue
                initializer = null
            }
        }
    ...
}

SynchronizedLazyImpl 重写了 value 属性的 getter 方法,其中会判断 value 是否有初始化过,已经初始化就直接返回结果,未初始化才执行 initializer,所以,这就是 by lazy{} 的初始化逻辑只会执行一次的原因。前面说过,Delegate 对象需要有 getValue()setValue() 方法,那么 Lazy 的这 2 个方法在哪呢?按住 ctrl +鼠标左键,点击 by 可以找到:

// Lazy.kt
@kotlin.internal.InlineOnly
public inline operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value

发现,Lazy 只有 getValue(),没有 setValue(),这不难理解,var 变量延迟初始化使用的是 lateinit,val 常量使用的 by lazy,val 常量不可修改,所以不需要提供 setValue()

#可观察属性 Observable

除了 by lazy 外,Kotlin 为还提供了可观察属性 Observable,这个属性代理可以监听属性值的变化,需要通过 by Delegates.observable() 来设置,这是 Delegates.observable() 的源码,具体实现就不深入了,该函数要求传入 2 个参数,分别是初始值 initialValue 和用于监听值变化的 lambda 表达式:

// Delegates.kt
public object Delegates {
    /**
    * Returns a property delegate for a read/write property that calls a specified callback function when changed.
    * @param initialValue the initial value of the property.
    * @param onChange the callback which is called after the change of the property is made. The value of the property
    *  has already been changed when this callback is invoked.
    *
    *  @sample samples.properties.Delegates.observableDelegate
    */
    public inline fun <T> observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit):
            ReadWriteProperty<Any?, T> =
        object : ObservableProperty<T>(initialValue) {
            override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(property, oldValue, newValue)
        }
    ...
}

下面我们来使用 Delegates.observable() 监听属性值的变化:

class Animal {
    var gender: Boolean by Delegates.observable(true) { prop, old, new ->
        println("prop = ${prop.name}, old = $old, new = $new")
    }
}

fun main() {
    val animal = Animal()
    println(animal.gender) // true
    animal.gender = false // prop = gender, old = true, new = false
    println(animal.gender) // false
}

可以看到,当修改了属性值时,会触发 Delegates.observable() 参数 2 的 lambda 表达式,输出了相应的日志信息,随后属性值也发生了改变。Kotlin 还提供了 Delegates.vetoable() 可观察属性代理,vetoableobservable 要强大一点,它除了能监听属性值变化,还可以控制属性是否要修改,通过查看 Delegates.vetoable() 源码注释可以知道,其参数 2 的 lambda 表达式(也就是 callback)返回值将决定属性值是否被更新:

// Delegates.kt
public object Delegates {
    /**
     * Returns a property delegate for a read/write property that calls a specified callback function when changed,
     * allowing the callback to veto the modification.
     * @param initialValue the initial value of the property.
     * @param onChange the callback which is called before a change to the property value is attempted.
     *  The value of the property hasn't been changed yet, when this callback is invoked.
     *  If the callback returns `true` the value of the property is being set to the new value,
     *  and if the callback returns `false` the new value is discarded and the property remains its old value.
     *
     *  @sample samples.properties.Delegates.vetoableDelegate
     *  @sample samples.properties.Delegates.throwVetoableDelegate
     */
    public inline fun <T> vetoable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Boolean):
            ReadWriteProperty<Any?, T> =
        object : ObservableProperty<T>(initialValue) {
            override fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = onChange(property, oldValue, newValue)
        }
    ...
}

我们将上面例子中的 gender 属性代理修改成 Delegates.vetoable(),并在 callback 中返回 false,以阻止属性值的修改:

class Animal {
    var gender: Boolean by Delegates.vetoable(true) { prop, old, new ->
        println("prop = ${prop.name}, old = $old, new = $new")
        false // lambda表达式最后语句的结果作为返回值
    }
}

fun main() {
    val animal = Animal()
    println(animal.gender) // true
    animal.gender = false // prop = gender, old = true, new = false
    println(animal.gender) // true
}

至此属性代理的原理及常用属性代理就讲解完了,如需了解更多属性代理的应用,请点击:https://www.kotlincn.net/docs/reference/delegated-properties.html

目录
相关文章
|
4天前
|
存储 人工智能 弹性计算
阿里云弹性计算_加速计算专场精华概览 | 2024云栖大会回顾
2024年9月19-21日,2024云栖大会在杭州云栖小镇举行,阿里云智能集团资深技术专家、异构计算产品技术负责人王超等多位产品、技术专家,共同带来了题为《AI Infra的前沿技术与应用实践》的专场session。本次专场重点介绍了阿里云AI Infra 产品架构与技术能力,及用户如何使用阿里云灵骏产品进行AI大模型开发、训练和应用。围绕当下大模型训练和推理的技术难点,专家们分享了如何在阿里云上实现稳定、高效、经济的大模型训练,并通过多个客户案例展示了云上大模型训练的显著优势。
|
8天前
|
存储 人工智能 调度
阿里云吴结生:高性能计算持续创新,响应数据+AI时代的多元化负载需求
在数字化转型的大潮中,每家公司都在积极探索如何利用数据驱动业务增长,而AI技术的快速发展更是加速了这一进程。
|
5天前
|
人工智能 运维 双11
2024阿里云双十一云资源购买指南(纯客观,无广)
2024年双十一,阿里云推出多项重磅优惠,特别针对新迁入云的企业和初创公司提供丰厚补贴。其中,36元一年的轻量应用服务器、1.95元/小时的16核60GB A10卡以及1元购域名等产品尤为值得关注。这些产品不仅价格亲民,还提供了丰富的功能和服务,非常适合个人开发者、学生及中小企业快速上手和部署应用。
|
13天前
|
人工智能 弹性计算 文字识别
基于阿里云文档智能和RAG快速构建企业"第二大脑"
在数字化转型的背景下,企业面临海量文档管理的挑战。传统的文档管理方式效率低下,难以满足业务需求。阿里云推出的文档智能(Document Mind)与检索增强生成(RAG)技术,通过自动化解析和智能检索,极大地提升了文档管理的效率和信息利用的价值。本文介绍了如何利用阿里云的解决方案,快速构建企业专属的“第二大脑”,助力企业在竞争中占据优势。
|
15天前
|
自然语言处理 数据可视化 前端开发
从数据提取到管理:合合信息的智能文档处理全方位解析【合合信息智能文档处理百宝箱】
合合信息的智能文档处理“百宝箱”涵盖文档解析、向量化模型、测评工具等,解决了复杂文档解析、大模型问答幻觉、文档解析效果评估、知识库搭建、多语言文档翻译等问题。通过可视化解析工具 TextIn ParseX、向量化模型 acge-embedding 和文档解析测评工具 markdown_tester,百宝箱提升了文档处理的效率和精确度,适用于多种文档格式和语言环境,助力企业实现高效的信息管理和业务支持。
3936 2
从数据提取到管理:合合信息的智能文档处理全方位解析【合合信息智能文档处理百宝箱】
|
4天前
|
算法 安全 网络安全
阿里云SSL证书双11精选,WoSign SSL国产证书优惠
2024阿里云11.11金秋云创季活动火热进行中,活动月期间(2024年11月01日至11月30日)通过折扣、叠加优惠券等多种方式,阿里云WoSign SSL证书实现优惠价格新低,DV SSL证书220元/年起,助力中小企业轻松实现HTTPS加密,保障数据传输安全。
503 3
阿里云SSL证书双11精选,WoSign SSL国产证书优惠
|
11天前
|
安全 数据建模 网络安全
2024阿里云双11,WoSign SSL证书优惠券使用攻略
2024阿里云“11.11金秋云创季”活动主会场,阿里云用户通过完成个人或企业实名认证,可以领取不同额度的满减优惠券,叠加折扣优惠。用户购买WoSign SSL证书,如何叠加才能更加优惠呢?
985 3
|
8天前
|
机器学习/深度学习 存储 人工智能
白话文讲解大模型| Attention is all you need
本文档旨在详细阐述当前主流的大模型技术架构如Transformer架构。我们将从技术概述、架构介绍到具体模型实现等多个角度进行讲解。通过本文档,我们期望为读者提供一个全面的理解,帮助大家掌握大模型的工作原理,增强与客户沟通的技术基础。本文档适合对大模型感兴趣的人员阅读。
412 17
白话文讲解大模型| Attention is all you need
|
8天前
|
算法 数据建模 网络安全
阿里云SSL证书2024双11优惠,WoSign DV证书220元/年起
2024阿里云11.11金秋云创季火热进行中,活动月期间(2024年11月01日至11月30日),阿里云SSL证书限时优惠,部分证书产品新老同享75折起;通过优惠折扣、叠加满减优惠券等多种方式,阿里云WoSign SSL证书将实现优惠价格新低,DV SSL证书220元/年起。
560 5
|
4天前
|
安全 网络安全
您有一份网络安全攻略待领取!!!
深入了解如何保护自己的云上资产,领取超酷的安全海报和定制鼠标垫,随时随地提醒你保持警惕!
697 1
您有一份网络安全攻略待领取!!!