IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Kotlin的委托机制 -> 正文阅读

[移动开发]Kotlin的委托机制

Kotlin 的委托机制在语言层面自动实现了 Java 的组合代理。Kotlin 的委托包括委托类、委托属性,使用 by 关键字表示委托。

委托类

假设有一个接口类 Db,用来保存数据。

interface Db {
    fun save()
}

Db 有两个具体的实现类 SqlDb 和 GreenDaoDb

class SqlDb : Db {
    override fun save() {
        println("save sql db")
    }
}

class GreenDaoDb : Db {
    override fun save() {
        println("save green dao db")
    }
}

如果使用 Java 风格的委托方式,会这么写:

class MyDb(private val db: Db) : Db {
    override fun save() {
        db.save()
    }
}

这种方式有很多样板代码,比如重写 save(),在接口方法调用属性 db 的 save()。
?

Kotlin 的 委托机制使用 by 关键字,自动帮我们省去了样板代码。
?

只需要使用 by 关键字表明委托给了谁。

class UniversalDb(db: Db) : Db by db

Db 接口的所有方法都交给 by 关键字后面的 db 实现。
?

它等价于:

class UniversalDb(private val db: Db) : Db {
    override fun save() {
        db.save()
    }
}

?

委托类的使用如下:

fun main() {
    // delegate class
    println("\ndelegate class")
    UniversalDb(SqlDb()).save()
    UniversalDb(GreenDaoDb()).save()
}

将任意一种 Db 接口的实现类作为委托类 UniversalDb 的入参,UniversalDb 的 save 方法实际是由入参实现的。
?

总结:委托类帮我们自动实现了接口的方法。
?

?

?

委托属性

如果说委托类实现了接口方法,那么委托属性实现了属性的 getter/setter 方法。
?

Item 类将它的 total 属性委托给了 count 属性。

/**
 * 委托属性
 * 两个属性之间的直接委托。Kotlin 1.4新特性
 */
class Item {
    var count: Int = 0
    var total: Int by ::count
}

注意委托属性的 by 关键字后面是一个双冒号 :: ,表示属性引用操作符。
?

假如不写 by ::count,而是写 by count,编译器会报错提示:

class Item {
    var count: Int = 0
    var total: Int by count // 报错
}
Type 'Int' has no method 'getValue(Item, KProperty<*>)' and thus it cannot serve as a delegate
Type 'Int' has no method 'setValue(Item, KProperty<*>, Int)' and thus it cannot serve as a delegate for var (read-write property) 

编译器提示 Int 类型没有实现 getValue 和 setValue 方法,不能作为委托属性。
?

而 ::count 属性引用是 KMutableProperty0 类,它帮我们实现了 get 和 set。

item 的 count 值改变时,委托属性 total 也跟着改变。

fun main() {
    // delegate property
    println("\ndelegate property")
    val item = Item()
    item.count = 2
    println("total:${item.total}")

}

?

懒加载委托

kotlin 提供了 lazy 方法实现懒加载委托,也就是只在 data 第一次被使用的时候才开始加载。

/**
 * 懒加载委托
 */
val data: String by lazy {
    request()
}

fun request(): String {
    println("执行网络请求")
    return "网络数据"
}

?

第一次 println(data) 会打印 “执行网络请求”,而第二次不会。因为使用 lazy 方法将 request 的结果保存,第二次打印 data 时,直接返回 reqeust 结果,而不是再次执行 request。

fun main() {
    // lazy delegate
    println("\nlazy delegate")
    println("开始")
    println(data)
    println(data)
}
lazy delegate
开始
执行网络请求
网络数据
网络数据

kotlin 的 lazy 方法定义在 LazyJVM.kt,返回一个 SynchronizedLazyImpl 类的示例。

public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)

SynchronizedLazyImpl 实现了一个单例模式,如果 _value 初始化过,直接返回 _value 的值,否则使用 lazy 的入参函数 initializer 初始化,返回 T 类型的值,并赋值给 _value。

private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
    private var initializer: (() -> T)? = initializer
   ...

    override val value: T
        get() {
            val _v1 = _value
            if (_v1 !== UNINITIALIZED_VALUE) {
                ...
                return _v1 as T
            }

            return synchronized(lock) {
                val _v2 = _value
                if (_v2 !== UNINITIALIZED_VALUE) {
                    @Suppress("UNCHECKED_CAST") (_v2 as T)
                } else {
                    val typedValue = initializer!!()
                    _value = typedValue
                    initializer = null
                    typedValue
                }
            }
        }
}

自定义委托

除了标准的委托属性,kotlin 还提供了自定义委托的方式实现委托属性。
?

以 Owner 为例,它的 text 属性委托给了 StringDelegate,textReadWrite 属性委托给了 StringReadWritePropertyDelegate。这两个都是自定义委托。
?

需要注意的是两者都使用了括号 () 操作符,因为委托需要指定一个对象示例。

class Owner {
    var text: String by StringDelegate()
    var textReadWrite: String by StringReadWritePropertyDelegate()
}

StringDelegate 实现了 getValue 和 setValue 方法,用来实现被委托属性的 getter 和 setter。 getValue 和 setValue 方法的入参、返回值等签名信息必须严格按照格式来写,否则编译不过。
?

thisRef 表示属性所在的类,property 表示被委托的属性,value 表示 setter 需要设置给属性的值。

/**
 * 自定义委托
 */
class StringDelegate(private var s: String = "Hello") {

    /**
     * getValue 操作符
     * thisRef 属性所在的类的类型
     * KProperty 表示一个属性,例如命名的 val 或者 var 声明。此类的实例可由 :: 运算符获取
     * 返回值 String
     */
    operator fun getValue(thisRef: Owner, property: KProperty<*>): String {
        return s
    }

    /**
     * setValue 操作符
     * thisRef 属性所在的类的类型
     * KProperty 表示一个属性,例如命名的 val 或者 var 声明。此类的实例可由 :: 运算符获取
     * value 属性的值,类型为 String
     */
    operator fun setValue(thisRef: Owner, property: KProperty<*>, value: String) {
        s = value
    }
}

?

为了简化自定义委托的实现,kotlin 提供了一些标准属性接口。
?

ReadWriteProperty

?

ReadWriteProperty 可读写属性接口,它的签名和上面自己写的 StringDelegate 是一样的。

/**
 * 使用 ReadWriteProperty接口实现委托属性,使用了泛型
 */
class StringReadWritePropertyDelegate(private var s: String = "Hello custom") :
    ReadWriteProperty<Owner, String> {
    override fun getValue(thisRef: Owner, property: KProperty<*>): String = s

    override fun setValue(thisRef: Owner, property: KProperty<*>, value: String) {
        s = value
    }
}

?

ReadOnlyProperty

?

如果是只读属性,可以实现 ReadOnlyProperty。

public fun interface ReadOnlyProperty<in T, out V> {
    /**
     * Returns the value of the property for the given object.
     * @param thisRef the object for which the value is requested.
     * @param property the metadata for the property.
     * @return the property value.
     */
    public operator fun getValue(thisRef: T, property: KProperty<*>): V
}

?

使用自定义委托:

    // custom delegate property
    println("\ncustom property delegate")
    val owner = Owner()
    println(owner.text)
    println(owner.textReadWrite)

输出如下:

custom property delegate
Hello
Hello custom

PropertyDelegateProvider

除了上述可读写、只读的委托属性,Kotlin 还提供了委托属性提供者 PropertyDelegateProvider,用来提供委托属性。
?

SmartDelegator 实现了 PropertyDelegateProvider 的 provideDelegate,根据属性名称动态返回委托属性。

class SmartDelegator : PropertyDelegateProvider<Owner, StringDelegate> {
    override fun provideDelegate(thisRef: Owner, property: KProperty<*>): StringDelegate {
        return if (property.name.contains("log")) {
            StringDelegate("log")
        } else {
            StringDelegate("cat")
        }
    }
}

?

使用方法和委托属性一样:

class Owner {
    var log by SmartDelegator()
    var cat by SmartDelegator()
    var text: String by StringDelegate()
    var textReadWrite: String by StringReadWritePropertyDelegate()
}

fun main() {
    // provide delegate
    println(owner.log)
    println(owner.cat)
}

根据属性名称输出:

log
cat

总结

kotlin 使用 by 关键字实现了委托模式。kotlin 的委托有委托类、委托属性、懒加载委托、自定义委托。
?

自定义委托有 ReadOnlyProperty、ReadWriteProperty、PropertyDelegateProvider 三个接口,方便快速实现委托属性。
?

使用委托可以简化样板代码,写起来更加简洁。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-01-29 23:11:58  更:2022-01-29 23:13:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 12:44:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码