Kotlin学习快速入门(8)—— 委托

原文地址:Kotlin学习快速入门(8)—— 属性委托 – Stars-One的杂货小窝
委托其实是一种设计模式,但Kotlin把此特性编写进了语法中,可以方便开发者快速使用

委托对应的关键字是 by

属性委托

先讲下属性委托吧,首先,复习下kotlin中设置set和get方法

默认的set和get我们可以隐藏,实际上一个简单的类代码如下:

class Person {
    var personName = ""
    // 这是默认的 get/set(默认是隐藏的)
    get() = field
    set(value) {
        field = value
    }
}

这里具体知识点可以查看之前所说Kotlin学习快速入门(3)——类 继承 接口 – Stars-One的杂货小窝

当然,如果是数据bean类,我们会将get和set方法隐藏(或者使用 data关键字来声明一个数据类)

若我们需要在get或set方法的时候做一下逻辑处理,比如说上面的 personName字段,我们只允许 接收长度小于等于10的字符串,超过10长度的字符串就不接收(即不设置新数值),则是应该这样写:

class Person{
    var personName = ""
        // 这是重写的 get/set
        get() = "PersonName $field"
        set(value) {
            field = if (value.length

然后,我们再延伸出来,如果此规则不止应用于personName字段,还可用到其他类的字段中,这个时候就是使用到属性委托。

简单描述: 我们将此规则抽取出来,需要应用到此规则的字段的get/set方法委托给规则去做,这就叫属性委托

延迟加载(懒加载)

在开始讲属性委托之前,先说明下延迟加载

Kotlin中提供了lazy方法,使用 by+ lazy{}联用,我们就实现延迟加载(也可称作懒加载)

fun main() {

    val demo = Demo()
    val textContent = demo.textContent
    val result = demo.textContent.substring(1)
    println(result)
    println("打印:$textContent")
}

class Demo{

    val textContent by lazy { loadFile() }

}
fun loadFile(): String {
    println("读取文件...")
    //模拟读取文件返回数据
    return "读取的数据"
}

这里的关键词by出现在属性名后面,表示属性委托,即将属性的读和写委托给另一个对象,被委托的对象必须满足一定的条件:

  • 对于 val 修饰的只读变量进行属性委托时,被委托的对象必须实现 getValue()接口,即定义如何获取变量值。
  • 对于 var 修饰的读写变量进行属性委托时,被委托对象必须实现 getValue()setValue()接口,即定义如何读写变量值。

lazy()方法,接收一个lambda函数,返回值是一个Lazy对象,所以就可以简写成上面的样子,其只实现了 getValue()接口,所以,当你尝试将 textContent改为var类型,IDE会提示报错!!

也是因为这点,属于延迟加载的字段,是不可被再次修改了,所以采用 lazy懒加载的方式,其实就是单例模式

Delegates.vetoable

还记得上述我们要实现的规则吗,其实Kotlin中已经有了几个默认的委托规则供我们快速使用(上述的lazy其实也是一个)

Delegates.vetoable()的规则就是上述规则的通用封装,解释为:

但会在属性被赋新值生效之前会传递给 Delegates.vetoable()进行处理,依据 Delegates.vetoable()的返回的布尔值判断要不要赋新值。

如下面例子:

class Person {
    var personName by Delegates.vetoable("") { property, oldValue, newValue ->
        //当设置的新值满足条件,则会设置为新值
        newValue.length

Delegates.notNull

设置字段不能为null,不过想不到具体的应用情景

class Person {
    var personName by Delegates.notNull()
}

Delegates.observable

使用 Delegates.observable可以帮我们快速实现观察者模式,只要字段数值发生改变,就会触发

class Person{
    var age by Delegates.observable(0){ property, oldValue, newValue ->
        //这里可以写相关的逻辑
        if (newValue >= 18) {
            tip = "已成年"
        }else{
            tip = "未成年"
        }
    }

    var tip =""
}

上面的例子就比较简单,设置age同时更新提示,用来判断是否成年

 val person = Person()
    person.age = 17
    println(person.tip)

补充-自定义委托

上述都是官方定义好的一些情形,但如果不满足我们的需求,这就需要自定义委托了

官方提供了两个基础类供我们自定义委托使用:

ReadWriteProperty 包含get和set方法,对应 var关键字
ReadOnlyProperty 只有get方法,对应 val关键字

PS:实际上,我们自己随意创建个委托类也是可以的,不过这样写不太规范,所以我们 一般直接实现官方给的上述两个类即可

Kotlin学习快速入门(8)—— 委托

ReadWriteProperty和ReadOnlyProperty都需要传两个泛型,分别为R,T

  • R 持有属性的类型
  • T 字段类型

可能上面描述不太明白,下面给个简单例子,Person类中有个name字段(String),首字母需要大写:

class Person {
    var name by NameToUpperCase("")
}

class NameToUpperCase(var value:String) :ReadWriteProperty<person,string>{
    //NameToUpperCase&#x7C7B;&#x4E2D;&#x9ED8;&#x8BA4;&#x6709;&#x4E2A;&#x5C5E;&#x6027;&#x5B57;&#x6BB5;&#xFF0C;&#x7528;&#x6765;&#x5B58;&#x6570;&#x636E;

    override fun getValue(thisRef: Person, property: KProperty<*>): String {
        return this.value
    }

    override fun setValue(thisRef: Person, property: KProperty<*>, value: String) {
        //&#x5728;&#x8BBE;&#x7F6E;&#x6570;&#x503C;&#x7684;&#x65F6;&#x5019;&#xFF0C;&#x5C06;&#x7B2C;&#x4E00;&#x4E2A;&#x5B57;&#x6BCD;&#x8F6C;&#x4E3A;&#x5927;&#x5199;&#xFF0C;&#x4E00;&#x822C;&#x63A8;&#x8350;&#x5728;setValue&#x91CC;&#x7F16;&#x5199;&#x903B;&#x8F91;
        this.value = value.substring(0,1).toUpperCase()+value.substring(1)
    }
}
</*></*></person,string>

个人看法,一般在setValue的时候进行设置数值比较好,因为getValue作操作的话,会触发多次,处理的逻辑复杂的话可能会浪费性能…

当然,再提醒下,上面的逻辑也可以直接去字段里的 setValue()里面改,委托只是方便抽取出去供其他类应用同样的规则

PS: 如果你的委托不是针对特定的类,R泛型可以改为Any

类委托

这个一般与多态一起使用,不过个人想不到有什么具体的应用情景,暂时做下简单的记录

interface IDataStorage{
    fun add()
    fun del()
    fun query()
}

class SqliteDataStorage :IDataStorage{
    override fun add() {
        println("SqliteDataStorage add")
    }

    override fun del() {
        println("SqliteDataStorage del")
    }

    override fun query() {
        println("SqliteDataStorage query")
    }

}

假如现在我们有个MyDb类,查询的方法与SqliteDataStorage这个里的方法有所区别,但其他方法都是没有区别,这个时候就会用到类委托了

有以下几种委托的使用方式

1.委托类作为构造器形参传入(常用)

class MyDb(private val storage:IDataStorage) : IDataStorage by storage{
    override fun add() {
        println("mydb add")
    }
}
val db = MyDb(SqliteDataStorage())
db.add()
db.query()

输出结果:

mydb add
SqliteDataStorage query

如果是全部都是委托给SqliteDataStorage的话,可以简写为这样:

class MyDb(private val storage:IDataStorage) : IDataStorage by storage

2.新建委托类对象

class MyDb : IDataStorage by SpDataStorage(){
    override fun add() {
        println("mydb add")
    }
}

这里测试的效果与上文一样,不在重复赘述

参考

Original: https://www.cnblogs.com/stars-one/p/16501954.html
Author: Stars-one
Title: Kotlin学习快速入门(8)—— 委托

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/609610/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • JavaWeb开发——软件国际化(动态元素国际化)

    软件国际化的第二个部分,就是动态元素国际化。 数值,货币,时间,日期等数据由于可能在程序运行时动态产生,所以无法像文字一样简单地将它们从应用程序中分离出来,而是需要特殊处理。Jav…

    Java 2023年5月29日
    081
  • 超酷的元素周期表

    【原文链接】:https://blog.tecchen.tech ,博文同步发布到博客园。由于精力有限,对文章的更新可能不能及时同步,请点击上面的原文链接访问最新内容。欢迎访问我的…

    Java 2023年6月6日
    084
  • MYSQL安装教程 详细版

    通过这个路径可以直接下载到mysql5.7的安装包 添加一下path路径,这样我们能从任何位置打开mysql 添加path路径 配置my.ini ,如果没有这个文件就新建一个 修改…

    Java 2023年6月9日
    098
  • 通过反射获取某个对象下的属性值,或通过父类获取

    java;collapse:true;;gutter:false; import org.slf4j.Logger; import org.slf4j.LoggerFactory;…

    Java 2023年6月6日
    079
  • javaweb实现下载

    实现代码 java代码 package com.kuang.servlet; &#x200B; import javax.servlet.ServletException;…

    Java 2023年6月13日
    073
  • 2022-7-7学习日记

    马士兵 String、StringBuffer SttingBuiler String 是final修饰的,不可变的,每次操作都会产生新的String对象 StringBuffer…

    Java 2023年6月6日
    065
  • SpringBoot+Mybatis配置多数据源,分包方式

    看了不少网上关于多数据源的配置,大致可分为两类,分包方式和通过切面方式; 第一个子项目ds01即时使用分包方式完成多数据源配置。 总结项目中出现的问题和解决办法: 数据库的连接信息…

    Java 2023年6月8日
    0111
  • Spring 源码(10)Spring Bean 的创建过程(1)

    Spring Bean的创建刚开始进行了一些准备工作,比如转换服务的初始化,占位符解析器的初始化, BeanDefinition元数据的冻结等操作,都是为了在创建Bean的过程中保…

    Java 2023年6月14日
    074
  • java面试——垃圾回收机制

    垃圾回收机制:——GC 初学java时。最经典的一句话是”java不像C,需要担心处理不用的内存,他有自己的垃圾回收,会自己处理的”,这是当时老师上课提过的…

    Java 2023年6月9日
    099
  • IO流思维导图

    IO流思维导图 IO思维导图总结 总览: 1.文件 public boolean createNewFile() :当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。 (几乎…

    Java 2023年6月6日
    086
  • Kafka 概述

    kafka 是一个为事件流而生的分布式消息系统,广泛应用于网页用户记录跟踪,IOT 设备,日志采集,系统监控等场景。 kafka 是用于构建实时数据管道和流应用程序。具有横向扩展,…

    Java 2023年6月8日
    0101
  • Seata-初体验以及避坑

    Seata是什么 这里引用官方解释 Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 …

    Java 2023年6月16日
    076
  • 一致性hash原理 看这一篇就够了

    ​ 在了解一致性哈希算法之前,最好先了解一下缓存中的一个应用场景,了解了这个应用场景之后,再来理解一致性哈希算法,就容易多了,也更能体现出一致性哈希算法的优点,那么,我们先来描述一…

    Java 2023年6月7日
    084
  • 项目管理和缺陷跟踪系统 Redmine

    Redmine 概述 Redmine 是用 Ruby 开发的基于 web 的项目管理软件,是用 ROR 框架开发的一套跨平台项目管理系统,支持多种数据库,有不少自己独特的功能,例如…

    Java 2023年6月7日
    080
  • mybatis-plus 自动代码生成

    java;gutter:true; package com.lookcoder.perpayadmin.util;</p> <p>import com.ba…

    Java 2023年5月30日
    085
  • Java-异常初步练习

    案例一: package com.esandinfo; /** * 自定义一个Exception类 */ class MyCustomException extends Runti…

    Java 2023年5月29日
    089
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球