Kotlin中的属性委托,用起来!

news/2024/3/29 15:16:11/文章来源:https://blog.csdn.net/yesemenglongyulong/article/details/129192957

上篇文章我们了解了Kotlin中的接口委托,还可以使用by关键字委托属性。

使用属性委托,委托负责处理对属性的get和set函数的调用。如果您需要跨其他对象重用getter/setter逻辑,这可能非常有用,并允许您轻松扩展功能,而不仅仅是简单的支持字段。

属性委托

让我们假设你有一个Product类,它是这样定义的:

class Product(price: String, oldPrice: String)

该类的price属性有一些格式化要求。设置price时,要确保前缀统一添加¥符号。另外,在更新价格时,您希望自动增加更新计数属性。

你可以实现如下所示的功能:

class Product(price: String, oldPrice: String) {var price: String = priceset(value) {field = "¥$value"updateCount++}var updateCount = 0
}

在这个过程中,如果需求改变了,oldPrice原价格也需要遵循这个规则怎么办?你可以复制/粘贴这个逻辑来编写一个自定义setter,但突然你会发现自己为每个属性编写相同的setter:

class Product(price: String, oldPrice: String) {var price: String = priceset(value) {field = "¥$value"updateCount++}var oldPrice: String = oldPriceset(value) {field = "¥$value"updateCount++}var updateCount = 0
}

这两个setter方法几乎相同,告诉您其中一个不应该存在。使用属性委托,我们可以通过将getter和setter委托给属性来复用代码。

就像类委托一样,您可以使用by来委托属性,当您使用属性语法时,Kotlin将生成使用委托的代码

class Product(price: String, oldPrice: String) {var price: String by PriceDelegate()var oldPrice: String by PriceDelegate()var updateCount = 0
}

伴随这这种变化,你已经把price属性和oldPrice属性委托给了PriceDelegate这个类了。我们来看下PriceDelegate这个类。

如果你只想委托getter,你的委托类需要实现ReadProperty<Any?, String>;如果你getter和setter都需要委托,代理类需要实现ReadWriteProperty<Any?, String>

在我们的例子中,PriceDelegate需要实现ReadWriteProperty<Any?String>,因为您希望在调用setter时执行格式统一。

class PriceDelegate : ReadWriteProperty<Any?, String> {private var formattedString: String = ""override fun getValue(thisRef: Any?,property: KProperty<*>): String {return formattedString}override fun setValue(thisRef: Any?,property: KProperty<*>,value: String) {formattedString = "¥$value"}}

您可能已经注意到在getter和setter函数中有两个额外的参数。

第一个参数是thisRef,表示包含该属性的对象。thisRef可以用于访问对象本身,例如检查其他属性或调用其他类函数。

第二个参数是==KProperty<*>==,它可用于访问委托属性上的元数据。

回顾一下需求,让我们使用thisRef来访问并增加updateCount属性。

override fun setValue(thisRef: Any?,property: KProperty<*>,value: String
) {if (thisRef is Product) {thisRef.updateCount++}formattedString = "¥$value"
}

底层原理

我们来理解下这是如何工作的,让我们看一下反编译的Java代码。

PriceDelegate对象的price和oldPrice委托属性的私有引用,以及包含所添加的逻辑的getter /setter是由Kotlin编译器生成代码。

public final class Product {// $FF: synthetic fieldstatic final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(Product.class, "price", "getPrice()Ljava/lang/String;", 0)), (KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(Product.class, "oldPrice", "getOldPrice()Ljava/lang/String;", 0))};@NotNullprivate final PriceDelegate price$delegate;@NotNullprivate final PriceDelegate oldPrice$delegate;private int updateCount;@NotNullpublic final String getPrice() {return this.price$delegate.getValue(this, $$delegatedProperties[0]);}public final void setPrice(@NotNull String var1) {Intrinsics.checkNotNullParameter(var1, "<set-?>");this.price$delegate.setValue(this, $$delegatedProperties[0], (String)var1);}//...
}

使用这个技巧,任何调用者都可以使用常规的属性语法访问委托的属性。

fun main(){val product : Product = Product("10","15")product.oldPrice = "20" // calls generated setter, increments countprintln("Update count is ${product.updateCount}")
}

实际应用

同在在设置页面会有控制开关的需求,每一个开关会定义一个属性,以获取和设置开关的状态,这时这个状态我们通常的做法是通过SharedPreferences存储和读取持久化状态,这里通常可以考虑开关属性通过属性委托的形式,痛过SharedPreferences把存储和读取。

代码如下:

class PreferenceDelegate<T>(val context: Context, val name: String, private val default: T) :ReadWriteProperty<Any?, T> {val prefs: SharedPreferences by lazy {context.getSharedPreferences("default", Context.MODE_PRIVATE)}override fun getValue(thisRef: Any?, property: KProperty<*>): T {return findPreference(name, default)}override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {putPreference(name, value)}private fun <T> findPreference(name: String, default: T): T = with(prefs) {val res: Any = when (default) {is Long -> getLong(name, default)is String -> getString(name, default)is Int -> getInt(name, default)is Boolean -> getBoolean(name, default)is Float -> getFloat(name, default)else -> throw IllegalArgumentException("This type can not be saved into Preferences")}res as T}private fun <T> putPreference(name: String, value: T) = with(prefs.edit()) {when (value) {is Long -> putLong(name, value)is String -> putString(name, value)is Int -> putInt(name, value)is Boolean -> putBoolean(name, value)is Float -> putFloat(name, value)else -> throw IllegalArgumentException("This type can be saved into Preferences")}.apply()}}

使用起来就简单了,所有走SharedPreferences的逻辑属性都可以通过委托PreferenceDelegate使用

private var timeMillion: String by PreferenceDelegate(this, "metaHost", "")private var noticeState: Int by PreferenceDelegate(context, "noticeState", 0)

总结

委托可以帮助您将任务委托给其他对象,并提供更好的代码重用。Kotlin编译器创建代码以允许您无缝地使用委托。Kotlin使用简单的by关键字语法来委托属性或类。在底层,Kotlin编译器生成支持委托所需的所有代码,而不会向公共API暴露任何更改。简单地说,Kotlin生成并维护委托所需的所有样板代码,换句话说,您可以将委托委托给Kotlin。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_73596.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VMware ESXi 7.0 Update 3k - 领先的裸机 Hypervisor (sysin Custom Image)

VMware ESXi 7.0 Update 3k - 领先的裸机 Hypervisor (sysin Custom Image) VMware ESXi 7.0 Update 3k Standard & All Custom Image for ESXi 7.0 U3k Install CD 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-esxi-7-u3/&#xff0c;查看最新版。原创作品…

ChatGPT爆火:AI崛起,这些职场人的机遇到了?

ChatGPT最近真的被全球吃瓜群众玩坏了&#xff01; 回答情感问题&#xff0c;编写代码&#xff0c;撰写slogan或脚本&#xff0c;甚至还被用于毕业生论文…… 这个连马斯克都由衷地称赞的ChatGPT&#xff0c;是一种全新的聊天机器人模型。上线2个月&#xff0c;就拥有了上亿活…

04--WXML

1、什么是WXML什么是Wxml呢&#xff1f;我们首先要介绍一下Html&#xff0c;Html的全称为HyperTextMarkup Language&#xff0c;翻译过来就是超文本标记语言&#xff0c;这种语言目前已经普遍用于前端开发&#xff0c;而wxml正是从html演变而来&#xff0c;它基于微信这个平台&…

SQL server设置用户只能访问特定数据库、访问特定表或视图

在实际业务场景我们可能需要开放单独用户给第三方使用&#xff0c;并且不想让第三方看到与业务不相关的表或视图&#xff0c;我们需要在数据库中设置一切权限来实现此功能&#xff1a; 1.设置用户只能查看数据库中特定的视图或表 1.创建用户名 选择默认数据库 服务器角色默认…

__stack_chk_fail问题分析

一、问题进程收到SIGABRT信号异常退出&#xff0c;异常调用栈显示__stack_chk_fail*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** Build fingerprint: Pico/A7H10/PICOA7H10:10/5.5.0/smartcm.1676912090:userdebug/dev-keys Revision: 0 ABI: arm64 Times…

Web前端学习:一

编辑器的基础使用 编辑器推荐使用&#xff1a; HBuilderx&#xff08;免费中文&#xff09;&#xff08;建议使用&#xff09; Sublime&#xff08;免费英文&#xff09; Sublime中文设置方法&#xff0c;下载语言插件&#xff1a; 1、进入Sublime后&#xff0c;ShiftCtrlP…

wait/notify方法 等待唤醒机制

线程正在运行&#xff0c;调用这个线程的wait()方法&#xff0c;这个线程就会进入一个集合进行等待(这个集合的线程不会争抢cpu)&#xff0c;此时线程的状态就是waiting 当有线程调用notify()方法的时候&#xff0c;就会从集合中挑选一个线程进入到排队队列里面 notifyAll就是…

【样式】轮播图样式 uview 版本 : “2.0.31“

![在这里插入图片描述](https://img-blog.csdnimg.cn/6cd568ce932b4ea7ae52f10365979680.png html <view class"addSwiperdiv"><image src"/static/66.png" mode"aspectFill" class"titleimg"></image><view c…

VC++ 解决dll库动态库加载失败问题(调用LoadLibrary加载失败)(附源码)

目录 1、动态加载dll库去调用库中的函数 1.1、调用系统dll库中未公开的接口 1.2、调用控件库中的注册接口向系统中注册该控件 2、LoadLibrary动态加载dll库失败的场景 2.1、自制安装包中遇到的LoadLibrary加载dll库失败问题 2.2、主程序底层模块调用LoadLibrary加载dll库…

秒杀测试案例 Java Redis Mysql

基于redis和MySQL乐观锁实现秒杀优惠券场景&#xff0c;一人一单。MySQL乐观锁改良控制不出现超卖和少卖问题&#xff0c;使用redisson分布式锁在用户维度加锁控制一人一单。 源码&#xff1a;https://github.com/hanhanhanxu/SeckillTest 文中图片看不清的地方可以鼠标右键-&…

谷歌外推留痕,谷歌搜索留痕快速收录怎么做出来的?

本文主要分享谷歌搜索留痕的收录效果是怎么做的&#xff0c;让你对谷歌留痕技术有一个全面的了解。 本文由光算创作&#xff0c;有可能会被修改和剽窃&#xff0c;我们佛系对待这样的行为吧。 谷歌搜索留痕快速收录怎么做出来的&#xff1f; 答案是&#xff1a;通过谷歌蜘蛛…

C语言-结构体对齐

详细说明参考博客 (1条消息) C语言结构体对齐&#xff0c;超详细&#xff0c;超易懂_haozigegie的博客-CSDN博客 (1条消息) #pragma pack详解_OuJiang2021的博客-CSDN博客_#pragma pack 以下个人理解总结 出现结构体对齐考虑的根本原因就是&#xff1a;【数据存取执行效率】…

Openwrt中动态IPV6 防火墙的正确设置方法

环境&#xff1a;光猫桥接公网IPV6 问题&#xff1a;动态IPV6地址不知道怎么设置防火墙 解决办法&#xff1a;模糊匹配前缀&#xff0c;特定后缀 背景&#xff1a;将家中光猫桥接后&#xff0c;获得了公网的IPV6地址&#xff0c;可以从外部用IPV6访问家中的设备&#xff0c;但I…

【AI写作】 机器人流程自动化 介绍 - Robotic Process Automation (RPA) Introduction

写一篇文章介绍RPA技术,未来的发展。使用markdown格式,有3级索引,超过3000字。 某位大师说过的: 任何行业、任何部门都有大量的场景,涉及重复、有规则逻辑的工作,都可以用 RPA 开发一个软件机器人帮助完成。 文章目录 机器人过程自动化(RPA)简介RPA的定义RPA的好处Robo…

【centos7下部署mongodb】

一.安装环境 CentOS7MongoDB4.0.13正式版。 二.下载MongoDB 1.1 官网下载地址&#xff1a;https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-4.0.13.tgz 1.2 将压缩包通过xftp上传到服务器/opt目录&#xff0c;然后解压、改名 三. 配置环境变量及配置文件 3.1配置系…

有限差分法求解不可压NS方程

网上关于有限差分法解NS方程的程序实现不尽完备&#xff0c;这里是一些补充注解 现有的优秀资料 理论向 【1】如何从物理意义上理解NS方程&#xff1f; - 知乎 【2】NS方程数值解法&#xff1a;投影法的简单应用 - 知乎 【3】[计算流体力学] NS 方程的速度压力法差分格式_…

pytorch1.2.0+python3.6

一、说明 pytorch1.2.0python3.6CUDA10.0cudnn7.4.1.5 二、步骤 在conda中创建一个新的虚拟环境 查看一下自己的所有环境 激活虚拟环境 conda activate torch1.2.0 关于cuda和cudnn 1、查看自己电脑系统是10.2版本 http://链接&#xff1a;https://pan.baidu.com/s/1v5cN6…

自学前端,你必须要掌握的3种定时任务

当你看到这篇博客的时候&#xff0c;一定会和狗哥结下不解之缘&#xff0c;因为狗哥的博客里不仅仅有代码&#xff0c;还有很多代码之外的东西&#xff0c;如果你可以看到最底部&#xff0c;看到投票环节&#xff0c;我相信你一定感觉到了&#xff0c;狗哥的真诚&#xff0c;狗…

DateTimeParseException

前端请求为字符串的时间格式2023-02-16 19:19:51&#xff0c;服务端用LocalDateTime类型接收时报解析异常java.time.format.DateTimeParseException: Text 2023-02-16 19:19:51 could not be parsed at index 10方法一&#xff1a;JsonFormat(shape Shape.STRING, pattern &q…

Redis 主从复制-服务器搭建【薪火相传/哨兵模式】

Redis 安装参考文章&#xff1a;Centos7 安装并启动 Redis-6.2.6 注意&#xff1a;本篇文章操作&#xff0c;不能在 静态IP地址 下操作&#xff0c;必须是 动态IP地址&#xff0c;否则最后主从服务器配置不成功&#xff01; 管道符查看所有redis进程&#xff1a;ps -ef|grep re…