委托

简单委托

如果把接口抽象类理解为是方法抽象层面一致性委托则可以认为是解决了方法实现层面一致性。更直接的,它本质上是允许我们在实现层面上进行复用。

本应由c属性实现的方法的委托给已经实现接口的a属性b属性

interface A {
    fun a()
}

interface B {
    fun b()
}

var a = object : A {
    override fun a() {
        println("a function delegated by  ${this}")
    }
}
var b = object : B {
    override fun b() {
        println("b function delegated by  ${this}")
    }
}

fun main(args: Array<String>) {

    var c = object : A by a, B by b{ } 
    c.a() //打印 "a function delegated by  doulala.kotlin.demo.delegation.DelegationKt$a"
    c.b() //打印 "b function delegated by  doulala.kotlin.demo.delegation.DelegationKt$b"
}

实验1:如果只想委托部分方法,关键方法自己实现。

var d = object : all {     
    override fun a() {
        //donothing
        println("d.a function do nothing")  
    }

    override fun b() {
        println("d.b function delegated by  ${this}")
    }
}

fun main(args: Array<String>) {

    var c = object : all by d {
        override fun a() {  //override d实例的 a()方法
            println("c.a function override d.a function")
        }
    }
    c.b()  //委托给d.b(), 输出"d.b function delegated by  doulala.kotlin.demo.delegation.DelegationKt$d"
    c.a()  //最终将调用 c.a()方法,输出"c.a function override d.a function"
}


实验2,如果同时继承多个接口,接口方法名一样,会先调用哪个? 会报错

interface A {
    fun a()
    fun samename()
}

interface B {
    fun b()
    fun samename()
}

var a = object : A {
    override fun samename() {
        println("samename function in a")
    }

    override fun a() {
        println("a function ")
    }
}
var b = object : B {
    override fun samename() {
        println("samename function in b")
    }

    override fun b() {
        println("b function ")
    }
}

fun main(args: Array<String>) {

    var c = object : A by a, B by b{} //会报错,被提示实现了多次
    c.a()
    c.b()
    c.samename()
    
}

  1. 委托(Delegation)的关键词是 by
  2. 委托可以通过重写方法控制委托的范围
  3. 委托时会严格校验被委托的方法来源,如果出现了相同的方法,会报错(这里与继承的判断是有区别的) 。限制就是安全所在。

属性委托

系统为我们提供了一些常用的属性委托,包括:

  • 延迟属性(lazy properties): 其值只在首次访问时计算;
  • 可观察属性(observable properties): 监听器会收到有关此属性变更的通知;
  • 把多个属性储存在一个映射map中,而不是每个存在单独的字段中。

延迟属性 lazy()

  1. lazy属性主要提供了只读属性——通过计算后设置属性值的能力

  2. 他提供了三种模式处理线程安全,包括 -

  • 用于多线程间需要同步的LazyThreadSafetyMode.SYNCHRONIZED
  • 如存在多线程使用,但是无需同步的LazyThreadSafetyMode.PUBLICATION -
  • 单线程场景的的LazyThreadSafetyMode.NONE
fun main(args: Array<String>) {
    val p: String by lazy(LazyThreadSafetyMode.NONE) {
        println("get() progress")
        "value"  //lazy其实是重写了get()方法,最后一行是p的值
    }
}

我们看一下他的源码,发现Lazy的核心就是重写属性的get()方法:

public fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
        when (mode) {
            LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
            LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
            LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
        }

//最简单的是UnsafeLazyImpl,单线程模式
internal class UnsafeLazyImpl<out T>(initializer: () -> T) : Lazy<T>, Serializable {
    private var initializer: (() -> T)? = initializer  //拿到初始化的lambda方法
    private var _value: Any? = UNINITIALIZED_VALUE //定义了一个非法值

    override val value: T
        get() {         //这行应该和上面一行连起来看,就是重写该属性的get()方法
            if (_value === UNINITIALIZED_VALUE) {
                _value = initializer!!() //初始化
                initializer = null
            }
            @Suppress("UNCHECKED_CAST")
            return _value as T //返回初始化后的值
        }

    override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE // 这个是Lazy的方法,用于记录是否初始化结束。

    override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."  //重写toString方法

    private fun writeReplace(): Any = InitializedLazyImpl(value) //未找到方法作用
}


可观察属性 Observable

Delegates一共提供两个代理工厂observablevetoable 方法

observable委托对象可以对数值的变化进行监控。共需要提供两个参数

  1. 参数A:initialValue: T 用于设置初始值
  2. 参数B:(property: KProperty<*>, oldValue: T, newValue: T) -> Unit 监控并获取对象引用变更前数据变更后数据
var name: String by Delegates.observable("init") { prop, old, new ->
    println("old : $old")
    println("new : $new")
}

fun main(args: Array<String>) {
    name = "123"
    name = "456"
}

vetoable委托对象可以对数值的变化进行控制。与observable唯一的不同是,参数B的返回值变为了Boolean,如果返回值为true接受变化,如果为false不接受变化。

var age: Int by Delegates.vetoable(18) { prop, old, new ->
    println("old : $old")
    println("new : $new")
    val accept=new>16
    println("accept : $accept")
    accept //只接受>16的变更
}


fun main(args: Array<String>) {
    age = 12
    println("age =$age") //输出: "age =18"
    age = 20
    println("age =$age")//输出: "age =20"
}

解密属性委托

属性委托的原理是by了一个对象,这个对象其实是委托了getValuesetValue方法:


interface ReadOnlyProperty<in R, out T> {  //只读的Delegate
   operator fun getValue(thisRef: R, property: KProperty<*>): T
}


interface ReadWriteProperty<in R, T> {//读写的Delegate
   operator fun getValue(thisRef: R, property: KProperty<*>): T
   operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}

一个自定义的属性委托:

fun main(args: Array<String>) {

    var a: String by object : ReadWriteProperty<Any?, String> {
        var _value: String = "" //属性委托一般都需要一个间接对象进行数据读取与赋值
        
        override fun getValue(thisRef: Any?, property: KProperty<*>): String {
         
            print("in getValue")
            print("$thisRef")
            print("$property")
            return _value
        }
        override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {

            println("in setValue")
            _value = value
        }
    }
    a="1213"  //输出 “in setValue”
    println(a) //输出 in getValue null var a: kotlin.String  1213

}


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容

  • 1 类委托 Derived 的超类型列表中的 by句表示b 将会在 Derived 中内部存储。 并且编译器将成转...
    NiceDream阅读 1,539评论 1 3
  • 简述 在java中一些属性的具有相同的行为怎么办,抽象出类然后再去依赖调用,而在Kotlin中只需要一个by关键字...
    i校长阅读 2,628评论 0 4
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,654评论 18 139
  • Kotlin 属性 要讲 Kotlin 的委托属性,要先从 Kotlin 的属性说起,当然关于属性的定义就不多介绍...
    哦好么人阅读 11,547评论 3 11
  • 读书要不要有目的性?再想不明白就迟了! 蒋凤姣 读书要不要有目的性,一直是个辩证问题,正方反方一直没停止争论过。 ...
    龙城叶子阅读 1,276评论 0 0