kotlin 委托
一、类委托
interface DB{fun insert()
}
class SqliteDB : DB {override fun insert() {println(" SqliteDB insert")}
}class MySql : DB{override fun insert() {println(" MySql insert")}
}class OracleDB : DB{override fun insert() {println(" OracleDB insert")}
}class CreateDB(db:DB) : DB by dbfun main() {val db = CreateDB(MySql())db.insert()
}
运行main()方法:

CreateDB类需要实现的接口(DB)方法委托给了具体实现类(MySql、OracleDB、SqliteDB)来实现,我认为这和设计模式中的策略模式如出一辙。
通过AndroidStudio的自带工具
Tools -> kotlin -> show kotlin Bytecode ,然后点Decompile,将字节码进行反编译得到java代码
也能佐证我们的想法:

二、委托属性
private var floatValue = 789.12
private var num by ::floatValuefun main() {println("num:$num")num = 567.88println("floatValue:$floatValue")
}
运行结果:

这个示例中将属性num委托给了 属性floatValue,即对num进行get和set操作其实都作用在了floatValue上,因此会有上面的运行结果。
同样我们可以看下反编译后的代码:

让我们稍感惊讶的是,经过转换后甚至都没有定义num这个属性,只是增加了getNum和setNum两个方法来获取和改变floatValue的值。
三、自定义委托
方式1:
class Test3{var text:String by StringDelegate()
}class StringDelegate{operator fun getValue(owner:Any,property:KProperty<*>):String{return "delegate value"}operator fun setValue(owner:Any,property:KProperty<*>,value:String){println("value:$value")}
}fun main() {val t = Test3()println("text:${t.text}")t.text = "haha"
}
要点:
- 想要成为可接受委托的类需要实现运算符重载方法,若委托类实现了getValue和setValue(即可读可写),则被委托属性text可用var修饰;若委托类只实现了getValue(即只读),则被委托属性text可用val修饰。
- getValue的返回值,和setValue的value参数值类型要与被委托属性类型一致。
- getValue和setValue中的owner参数必须是被委托属性text所属的类或其父类。
方式2:
class Test3{var text:String by StringDelegate()var text2:String by StringDelegate2()
}class StringDelegate{operator fun getValue(owner:Any,property:KProperty<*>):String{return "delegate value"}operator fun setValue(owner:Any,property:KProperty<*>,value:String){println("value:$value")}
}class StringDelegate2:ReadWriteProperty<Any,String>{override fun getValue(thisRef: Any, property: KProperty<*>): String {return "delegate value2"}override fun setValue(thisRef: Any, property: KProperty<*>, value: String) {println("value2:$value")}}fun main() {val t = Test3()println("text:${t.text}")t.text = "haha"println("text2:${t.text2}")t.text2 = "haha"
}
要点:
- 这种方式的原理就是方式一,只不过是系统Api给我们提供了一种相对方便的使用方式。
- 此方式需要出入两个泛型,第一个必须是被委托属性text所属的类或其父类,第二个是被委托属性的类型
- 若委托类支持可读可写则继承ReadWriteProperty,若只支持可读则继承ReadOnlyProperty
此方式原理:

方式3:
在上面的基础之上还有另一种方式,可以根据属性的一些特征来返回一个合适的委托实现类,有点类似于抽象工厂模式,根据特定的需求返回合适的实现,如下:
class Test3{var text:String by StringDelegate()var text2:String by StringDelegate2()var text3:String by StringDelegateProvide()
}class StringDelegate{operator fun getValue(owner:Any,property:KProperty<*>):String{return "delegate value"}operator fun setValue(owner:Any,property:KProperty<*>,value:String){println("value:$value")}
}class StringDelegate2:ReadWriteProperty<Any,String>{override fun getValue(thisRef: Any, property: KProperty<*>): String {return "delegate value2"}override fun setValue(thisRef: Any, property: KProperty<*>, value: String) {println("value2:$value")}}class StringDelegate3:ReadWriteProperty<Any,String>{override fun getValue(thisRef: Any, property: KProperty<*>): String {return "delegate value3"}override fun setValue(thisRef: Any, property: KProperty<*>, value: String) {println("value3:$value")}}class StringDelegateProvide{operator fun provideDelegate(thisRef: Any,property: KProperty<*>):ReadWriteProperty<Any,String>{return if (property.name.contains("text3")){StringDelegate3()}else{StringDelegate2()}}
}fun main() {val t = Test3()println("text:${t.text}")t.text = "haha"println("text2:${t.text2}")t.text2 = "haha"println("text3:${t.text3}")
}
四、使用案例
通过委托封装Mmkv存取值的过程
import com.blankj.utilcode.util.GsonUtils
import com.tencent.mmkv.MMKV
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KPropertyopen class MMKVDelegate<T>(private val mmkv: MMKV,private val cls: Class<T>,private val key: String,private val defaultValue: T
) : ReadWriteProperty<Any?, T> {@Suppress("UNCHECKED_CAST")override fun getValue(thisRef: Any?, property: KProperty<*>): T {return when {cls.isEnum -> {val enumValues = cls.enumConstantsval enumValue = mmkv.decodeString(key, null)enumValues?.firstOrNull { it.toString() == enumValue } ?: defaultValue}cls.isData || MMKVDelegatedClass::class.java.isAssignableFrom(cls) -> runCatching {GsonUtils.fromJson(mmkv.decodeString(key), cls)}.getOrNull() ?: defaultValuecls.isAssignableFrom(String::class.java) -> mmkv.decodeString(key, defaultValue as String) as Tcls.isAssignableFrom(Int::class.java) -> mmkv.decodeInt(key, defaultValue as Int) as Tcls.isAssignableFrom(Boolean::class.java) -> mmkv.decodeBool(key, defaultValue as Boolean) as Tcls.isAssignableFrom(Float::class.java) -> mmkv.decodeFloat(key, defaultValue as Float) as Tcls.isAssignableFrom(Long::class.java) -> mmkv.decodeLong(key, defaultValue as Long) as Tcls.isAssignableFrom(Double::class.java) -> mmkv.decodeDouble(key, defaultValue as Double) as Tcls.isAssignableFrom(ByteArray::class.java) -> mmkv.decodeBytes(key, defaultValue as ByteArray) as Telse -> throw IllegalArgumentException("Unsupported type.")}}override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {val ok = when {cls.isEnum -> mmkv.encode(key, value?.toString())cls.isData || MMKVDelegatedClass::class.java.isAssignableFrom(cls) -> mmkv.encode(key,GsonUtils.toJson(value))cls.isAssignableFrom(String::class.java) -> mmkv.encode(key, value as String)cls.isAssignableFrom(Int::class.java) -> mmkv.encode(key, value as Int)cls.isAssignableFrom(Boolean::class.java) -> mmkv.encode(key, value as Boolean)cls.isAssignableFrom(Float::class.java) -> mmkv.encode(key, value as Float)cls.isAssignableFrom(Long::class.java) -> mmkv.encode(key, value as Long)cls.isAssignableFrom(Double::class.java) -> mmkv.encode(key, value as Double)cls.isAssignableFrom(ByteArray::class.java) -> mmkv.encode(key, value as ByteArray)else -> throw IllegalArgumentException("Unsupported type.")}if (!ok) {throw MMKVDelegateException.EncodeException()}}
}
import kotlin.jvm.internal.Reflectionval <T> Class<T>.isData: Boolean get() = Reflection.getOrCreateKotlinClass(this).isData
假设我们在项目中需要在本地记录一些App行为信息
object AppBehavior {private const val TAG = "AppBehavior"private const val MMKV_ID = "AppBehavior"//记录App启动次数var appStartTime: Int by SettingValue(Int::class.java,Key.APP_START_TIME,0)/*** APP行为的属性委托* 值存储成功时,自动发送UserLocalSettingChangedEvent事件* @param cls 属性的类型* @param key 存储到MMKV的key* @param defaultValue 从MMKV获取失败时,返回的默认值*/private class SettingValue<T>(private val cls: Class<T>,private val key: Key,private val defaultValue: T) : MMKVDelegate<T>(MMKV.mmkvWithID(MMKV_ID), cls, key.name, defaultValue) {override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {runCatching {super.setValue(thisRef, property, value)}.onFailure {Log.e(TAG, it.toString())if (it !is MMKVDelegateException) {throw it}}}}
}
调用:
AppBehavior.appStartTime++
相关文章:
kotlin 委托
一、类委托 interface DB{fun insert() } class SqliteDB : DB {override fun insert() {println(" SqliteDB insert")} }class MySql : DB{override fun insert() {println(" MySql insert")} }class OracleDB : DB{override fun insert() {println(&quo…...
Stream流的中间方法
一.Stream流的中间方法 注意1:中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程 注意2:修改Stream流中的数据,不会影响原来集合或者数组中的数据 二.filter filter的主要用法是…...
【车载开发系列】ParaSoft单元测试环境配置(四)
【车载开发系列】ParaSoft单元测试环境配置(四) 【车载开发系列】ParaSoft单元测试环境配置(四) 【车载开发系列】ParaSoft单元测试环境配置(四)一. 如何设置过滤二. 如何设置静态扫描的规则三. 如何设置单…...
IDEA 设置自动定位文件
一、场景分析 IDEA 在使用的过程中,发现有时候,打开一个类,它并不能自动帮我们在左侧 Project 树中定位出文件,需要自己手动点击 瞄准 图标。很不方便。 二、解决方法 1、点击 瞄准 图标旁边的 竖三点 2、将 Alwasy Select Opene…...
Nature Machine Intelligence 基于强化学习的扑翼无人机机翼应变飞行控制
尽管无人机技术发展迅速,但复制生物飞行的动态控制和风力感应能力,仍然遥不可及。生物学研究表明,昆虫翅膀上有机械感受器,即钟形感受器campaniform sensilla,探测飞行敏捷性至关重要的复杂气动载荷。 近日࿰…...
[Web安全 网络安全]-XXE 外部实体注入攻击XML
文章目录: 一:前言 1.定义 1.1 XXE 1.2 XML可扩展标记语言 2.DDT文档类型定义 2.1 分类 2.2 元素element DTD元素 DTD属性 2.3 实体entity DTD实体类别 DTD实体声明引用 声明:内部 外部 参数实体 公共实体 引用:…...
8--苍穹外卖-SpringBoot项目中套餐管理 详解(二)
目录 删除套餐 需求分析和设计 代码开发 根据id查询套餐 mapper层 Service层 ServiceImpl层 Mapper层 批量删除套餐 mapper层 Service层 ServiceImpl层 Mapper层 SetmealMapper.xml 修改套餐 需求分析和设计 代码开发 起售停售套餐 需求分析和设计 代码开发…...
测试面试题:pytest断言时,数据是符点类型,如何断言?
在使用 Pytest 进行断言时,如果数据是浮点类型,可以使用以下方法进行断言: 一、使用pytest.approx pytest.approx可以用来比较两个浮点数是否近似相等。例如: import pytestdef test_float_assertion():result 3.14159expecte…...
Python与MongoDB交互
一、基本概念 MongoDB: 一个面向文档的数据库系统,使用BSON(Binary JSON)作为存储格式。集合(Collection): 类似于关系型数据库中的表,是文档的集合。文档(Document): MongoDB中的基…...
安卓AI虚拟女友项目开发的Android开发环境搭建
第五章:Android开发环境搭建与基础入门 5-1 项目讲解思路说明 本文是安卓AI数字虚拟人项目实战的第五章,开发安卓AI安卓版数字虚拟人的Android基础部分。 在本章中,我们将详细介绍如何搭建Android开发环境,包括Android Studio的…...
基于SpringBoot+Vue+MySQL的智能垃圾分类系统
系统展示 用户前台界面 管理员后台界面 系统背景 随着城市化进程的加速,垃圾问题日益凸显,不仅对环境造成污染,也给城市管理带来了巨大挑战。传统的垃圾分类方式不仅费时费力,而且手工操作容易出现错误,导致垃圾分类效…...
你的个人文件管理助手:AI驱动的本地文件整理工具
🌐 引言 在数字化时代,我们经常面临文件管理的挑战。电脑中的文件杂乱无章,寻找特定文件变得既费时又费力。幸运的是,现在有了一款名为本地文件整理器的神器,它利用AI技术帮助你快速、智能地整理文件,同时…...
【PyTorch】环境配置
框架介绍 Pytorch简介 2017年1月,FAIR(Facebook AI Research)发布了PyTorch。PyTorch是在Torch基础上用python语言重新打造的一款深度学习框架。Torch是采用Lua语言作为接口的机器学习框架,但因为Lua语言较为小众,导…...
枫叶MTS格式转换器- 强大、操作简单的MTS、M2TS视频转换工具供大家学习研究参考
一款功能强大、操作简单的MTS、M2TS视频转换工具,欢迎下载使用。 使用本MTS格式转换器可以帮助您将索尼和松下等摄像机录制的MTS、M2TS格式高清视频转换为其他流行的视频格式,如MP4、3GP、AVI、MPEG、WMV、ASF、MOV、RM、VCD、SVCD、DVD、MKV、FLV、SWF、MPG、MP3、WAV、WMA…...
Vscode把全部‘def‘都收起来的快捷键
在 VSCode 中,你可以使用以下快捷键来收起所有函数定义 (def): Windows/Linux: Ctrl K, Ctrl 0macOS: Cmd K, Cmd 0 这个快捷键组合会折叠当前文件中所有的代码块(包括所有函数和类定义)。你可以通过相同的快捷键再次展开这…...
Web和UE5像素流送、通信教程
一、web端配置 首先打开Github地址:https://github.com/EpicGamesExt/PixelStreamingInfrastructure 找到自己虚幻引擎对应版本的项目并下载下来,我这里用的是5.3。 打开项目找到PixelStreamingInfrastructure-master > Frontend > implementat…...
【YOLO目标检测电梯间电动车与人数据集】共4321张、已标注txt格式、有训练好的yolov5的模型
目录 说明图片示例 说明 数据集格式:YOLO格式 图片数量:4321 标注数量(txt文件个数):4321 标注类别数:2 标注类别名称:person、electricBicycle 数据集下载:电梯间电动车与人数据集 图片示例 数据…...
【网络安全】公钥基础设施
1. PKI 定义 1.1 公钥基础设施的概念 公钥基础设施(Public Key Infrastructure,简称PKI)是一种基于公钥密码学的系统,它提供了一套完整的解决方案,用于管理和保护通过互联网传输的信息。PKI的核心功能包括密钥管理、…...
云原生(四十一)| 阿里云ECS服务器介绍
文章目录 阿里云ECS服务器介绍 一、云计算概述 二、什么是公有云 三、公有云优缺点 1、优点 2、缺点 四、公有云品牌 五、市场占有率 六、阿里云ECS概述 七、阿里云ECS特点 阿里云ECS服务器介绍 一、云计算概述 云计算是一种按使用量付费的模式,这种模式…...
计算机网络:计算机网络体系结构 —— OSI 模型 与 TCP/IP 模型
文章目录 计算机网络体系结构OSI 参考模型TCP/IP 参考模型分层的必要性物理层的主要问题数据链路层的主要问题网络层的主要问题运输层的主要问题应用层的主要问题 分层思想的处理方法发送请求路由器转发接受请求发送响应接收响应 计算机网络体系结构 计算机网络体系结构是指将…...
Ctool架构深度解析:模块化开发工具集的高效实现方案
Ctool架构深度解析:模块化开发工具集的高效实现方案 【免费下载链接】Ctool 程序开发常用工具 chrome / edge / firefox / utools / windows / linux / mac 项目地址: https://gitcode.com/gh_mirrors/ct/Ctool 在程序开发过程中,开发者经常需要在…...
从Awesome List到实战:构建你的AI编程工作流与Vibe Coding环境
1. 从“Awesome List”到“Vibe Coding”实战指南:如何构建你的AI编程工作流如果你最近在GitHub上逛过,或者关注AI编程工具的圈子,大概率会刷到一个叫“Awesome Vibe Coding”的仓库。乍一看,它像是一个又一个AI工具和项目的简单罗…...
Argo CD 集成 Helmfile 插件:实现 GitOps 下复杂应用声明式部署
1. 项目概述与核心价值如果你正在使用 Argo CD 管理 Kubernetes 集群,并且你的应用清单是由 Helmfile 来编排的,那么travisghansen/argo-cd-helmfile这个项目很可能就是你一直在寻找的“粘合剂”。简单来说,它是一个专门为 Argo CD 设计的 He…...
AUTOSAR Wdg模块的两种“狗”:片内看门狗与SPI外挂看门狗配置异同点解析
AUTOSAR Wdg模块深度解析:片内与SPI外挂看门狗的工程实践指南 在汽车电子控制单元(ECU)开发中,看门狗(Watchdog)模块是确保系统可靠性的关键组件。AUTOSAR标准下的Wdg模块支持两种典型硬件架构——片内集成…...
【研报 A109】2026年脑机接口产业化专题报告:首个侵入式产品获批,医保完成赋码
摘要:脑机接口行业正迎来产业化应用的关键元年,2026年行业正式从实验室研究走向规模化商业化落地,当前行业处于导入期尾端、爆发前夜,非侵入式与半侵入式路径已率先打通商业化通道,侵入式则处于临床验证阶段。政策端&a…...
保姆级教程:彻底解决CondaHTTPError网络连接失败(附.condarc文件完整配置流程)
深度解析CondaHTTPError:从网络诊断到.condarc文件全配置指南 遇到CondaHTTPError: HTTP 000 CONNECTION FAILED错误时,很多开发者会感到束手无策。这个问题通常出现在国内网络环境下,尤其是公司内网、校园网或使用某些代理服务后。本文将带你…...
加州自动驾驶测试报告解读:数据背后的技术演进与行业趋势
1. 从加州数据看自动驾驶的“成绩单”:2021年测试报告深度解读每年年初,自动驾驶圈子里不少人都会习惯性地去翻看一份来自美国加州的“成绩单”——加州机动车辆管理局发布的年度自动驾驶车辆测试报告。这份报告就像一份公开的“期中考试”排名ÿ…...
NotebookLM深度绑定Google Drive的终极方案(含OAuth2作用域最小化清单+服务账号部署模板)
更多请点击: https://intelliparadigm.com 第一章:NotebookLM深度绑定Google Drive的终极方案(含OAuth2作用域最小化清单服务账号部署模板) NotebookLM 本地知识增强能力依赖于安全、稳定且权限精确的 Google Drive 数据接入。直…...
5分钟免费解锁iPhone激活锁:applera1n实用指南
5分钟免费解锁iPhone激活锁:applera1n实用指南 【免费下载链接】applera1n icloud bypass for ios 15-16 项目地址: https://gitcode.com/gh_mirrors/ap/applera1n 面对二手iPhone的激活锁界面,你是否感到束手无策?applera1n是一款专为…...
告别空转!用RT-Thread PM组件给你的IoT设备省电:从投票机制到外设管理的完整指南
告别空转!用RT-Thread PM组件给你的IoT设备省电:从投票机制到外设管理的完整指南 在电池供电的物联网设备开发中,功耗优化往往成为决定产品成败的关键因素。想象一下,一个部署在偏远地区的环境监测节点,如果因为功耗问…...
