当前位置: 首页 > news >正文

Kotlin快速入门系列10

Kotlin的委托

委托模式是常见的设计模式之一。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。与Java一样,Kotlin也支持委托模式,通过关键字by。

类委托

类的委托即一个类中定义的方法实际是调用另一个类的对象的方法来实现的。例如下面的Java实例:

class RealPrinter { // the "delegate"void print() {System.out.print("something");}
}class Printer { // the "delegator"RealPrinter p = new RealPrinter(); // create the delegate void print() {p.print(); // delegation}
}public class Main {// to the outside world it looks like Printer actually prints.public static void main(String[] args) {Printer printer = new Printer();printer.print();}
}

可以看到在Java代码中printer 最终其实调用了RealPrinter的方法。用kotlin表示则需要用到by关键字:

// 创建接口
interface Base {fun print()
}// 实现此接口的被委托的类
class BaseImpl(val x: Int) : Base {override fun print() { print(x) }
}// 通过关键字 by 建立委托类
class Derived(b: Base) : Base by bfun main(args: Array<String>) {val b = BaseImpl(10)Derived(b).print() // 输出 10
}

在 Derived 声明中,by 子句表示,将 b 保存在 Derived 的对象实例内部,而且编译器将会生成继承自 Base 接口的所有方法, 并将调用转发给 b。

属性委托

属性委托指的是一个类的某个属性值不是在类中直接进行定义,而是将其托付给一个代理类,从而实现对该类的属性统一管理。
属性委托的具体语法格式如下:

val/var <属性名>: <类型> by <表达式>

· var/val:属性类型(可变/只读)

· 属性名:属性名称

· 类型:属性的数据类型

· 表达式:委托代理类

by 关键字之后的表达式就是委托, 属性的 get()和set() 方法将被委托给这个对象的 getValue() 和 setValue() 方法。属性委托不必实现任何接口, 但必须提供 getValue() 函数(对于 var属性,还需要 setValue() 函数)。

定义被委托的类

该类需要包含 getValue() 方法和 setValue() 方法,且参数 thisRef 为进行委托的类的对象,prop 为进行委托的属性的对象。实例如下:

import kotlin.reflect.KProperty
// 定义包含属性委托的类,KProperty是个接口
class PropertyExample {var str: String by Delegate()
}// 委托的类
class Delegate {operator fun getValue(thisRef: Any?, property: KProperty<*>): String {return "$thisRef, 这里委托了 ${property.name} 属性"}operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {println("--- $thisRef 的 ${property.name} 属性赋值为 $value ---")}
}
fun main(args: Array<String>) {val example = PropertyExample()println(example.str)     // 访问该属性,调用 Delegate.getValue()example.str = "Google"   // 调用 Delegate.setValue()println(example.str)
}

对应的控制台输出结果为:

这里做一个简单的说明:

· thisRef:属性的拥有者;

· property:对属性的描述,是 KProperty<*> 类型或是它的父类;

· value:属性的值。

标准委托

Kotlin的标准库提供很多工厂方法来实现属性的委托:

· 延迟属性Lazy

通过 lazy 我们可以定义一个懒加载的属性,该属性的初始化不会再类创建的时候发生,而是在第一次用到它的时候赋值。

lazy() 是一个函数, 是接受一个 Lambda 表达式作为参数, 返回一个 Lazy <T> 实例的函数。其返回的实例可以作为实现延迟属性的委托:第一次调用 get() 会执行已传递给 lazy() 的 lamda 表达式并记录结果,后续调用 get() 只是返回记录的结果。

下面是kotlin的经典示例:

val lazyValue: String by lazy {println(" lazyValue print ")     // 第一次调用输出,第二次调用不执行"lazyValue print again"
}fun main(args: Array<String>) {println(lazyValue)   // 第一次执行,执行两次输出表达式println(lazyValue)   // 第二次执行,只输出返回值
}

对应的输出结果为: 

· 可观察属性Observable

observable,让属性在发生变动的时候可以被关注的地方观察到。可以用于实现观察者模式。

Delegates.observable() 函数接受两个参数: 第一个是初始化值, 第二个是属性值变化事件的响应器(handler)。

在属性赋值后会执行事件的响应器(handler),它有三个参数:被赋值的属性、旧值和新值:

import kotlin.properties.Delegatesclass ObserveUser {var name: String by Delegates.observable("初始值") {prop, old, new ->println("旧值:$old -> 新值:$new")}
}fun main(args: Array<String>) {val user = ObserveUser()user.name = "第一次赋值"user.name = "第二次赋值"
}

对应控制台输出为:

· 属性存储在映射中

常见的用法是在一个映射(map)里存储属性的值。这种情况经常出现在像解析 JSON 或者做其他"动态"事情的应用中。这种情况下,可以使用映射实例自身作为委托来实现委托属性。

class WebSite(val map: MutableMap<String, Any?>) {val company: String by mapval url: String by map
}fun main(args: Array<String>) {var map:MutableMap<String, Any?> = mutableMapOf("company" to "谷歌大法好","url" to "www.Google.com")val site = WebSite(map)println(site.company)println(site.url)println("--------------")map.put("company", "白度全广告")map.put("url", "www.baiduu.com")println(site.company)println(site.url)}

对应的输出结果为:

局部委托属性

局部变量可以声明为委托属性。比如使用lazy初始化一个局部变量:

fun example(computeFoo: () -> Foo) {val memoizedFoo by lazy(computeFoo)if (someCondition && memoizedFoo.isValid()) {memoizedFoo.doSomething()}
}

上述代码中,memoizedFoo 变量只会在第一次访问时计算。 如果 someCondition 失败,那么该变量根本不会计算。

属性委托的特点

对于只读属性(val属性), 它的委托必须提供一个getValue()函数。该函数接受以下参数:

· thisRef —— 必须与属性所有者类型(对于扩展属性——指被扩展的类型)相同或者是它的超类型;

· property —— 必须是类型 KProperty<*> 或其超类型。

这个函数必须返回与属性相同的类型(或其子类型)

对于一个值可变(mutable)属性(var属性),除getValue()函数之外,它的委托还必须再提供一个setValue()函数, 这个函数接受以下参数:

· property —— 必须是类型 KProperty<*> 或其超类型;

· new value —— 必须和属性同类型或者是它的超类型。

相关文章:

Kotlin快速入门系列10

Kotlin的委托 委托模式是常见的设计模式之一。在委托模式中&#xff0c;有两个对象参与处理同一个请求&#xff0c;接受请求的对象将请求委托给另一个对象来处理。与Java一样&#xff0c;Kotlin也支持委托模式&#xff0c;通过关键字by。 类委托 类的委托即一个类中定义的方…...

Docker中配置MySql环境

目录 一、简单安装 1. 首先从Docker Hub中拉取镜像 2. 启动尝试创建MySQL容器&#xff0c;并设置挂载卷。 3. 查看mysql8这个容器是否启动成功 4. 如果已经成功启动&#xff0c;进入容器中简单测试 4.1 进入容器 4.2 登录mysql中 4.3 进行简单添加查找测试 二、主从复…...

智慧文旅:驱动文化与旅游融合发展的新动力

随着科技的快速发展和人们生活水平的提高&#xff0c;文化和旅游的融合成为了时代发展的必然趋势。智慧文旅作为这一趋势的引领者&#xff0c;通过先进的信息技术手段&#xff0c;推动文化与旅游的深度融合&#xff0c;为产业的发展注入新的活力。本文将深入探讨智慧文旅如何成…...

wordpress怎么做产品展示站?推荐使用MOK主题和ent主题

大多数WordPress站点都是个人博客网站&#xff0c;主要以文章性质的图文为主。不过部分站长想要用WordPress搭建一个产品展示站&#xff0c;应该怎么做呢&#xff1f; 其实&#xff0c;WordPress可以用来建立各种各样的博客网站&#xff0c;包括个人博客、企业网站、商城、影视…...

8、应急响应-战前溯源反制主机蜜罐系统HFishHIDSElkeidWazuh

用途&#xff1a;个人学习笔记&#xff0c;欢迎指正 目录 背景&#xff1a; 一、潮源反制-平台部署-蜜罐-Hfish 二、溯源反制-平台部署-HIDS-Wazuh 三、溯源反制-平台部署-HlDS-Elkeid-hub 背景&#xff1a; 攻击者对服务器存在着各种威胁行为&#xff0c;作为安全人员&am…...

LeetCode:283. 移动零

283. 移动零 1&#xff09;题目2&#xff09;代码方法一&#xff1a;两层for循环方法二&#xff1a;使用双指针 3&#xff09;结果方法一结果方法二结果 1&#xff09;题目 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的…...

游戏开发丨基于Panda3D的迷宫小球游戏

文章目录 写在前面Panda3D程序设计程序分析运行结果系列文章写在后面 写在前面 本期内容 基于panda3d的迷宫中的小球游戏 所需环境 pythonpycharm或anacondapanda3d 下载地址 https://download.csdn.net/download/m0_68111267/88792121 Panda3D Panda3D是一种开放源代码…...

微信小程序 安卓/IOS兼容问题

一、背景 在开发微信小程序时&#xff0c;不同的手机型号会出现兼容问题&#xff0c;特此记录一下 二、安卓/IOS兼容问题总结 2.1、new Date()时间转换格式时&#xff0c;IOS不兼容 问题&#xff1a;在安卓中时间格式2024-1-31 10:10:10&#xff0c;但是在iOS中是不支持 &q…...

结构体--共用体--枚举 之难点——链表 奋力学习嵌入式的第十六天

结构体 注意&#xff1a; 1.结构体类型 可以定义在 函数里里面 但是此时作用域就被限定在该函数中 2.结构体定义形式 //形式一 限定一类型 后定义变量 struct stu { ... }; struct stu s; //形式二 定义类型的同时 定义变量 struct stu { ... }s1,s2,*s3,s4[10]; struc…...

猜凶手

日本某地发生了一件谋杀案&#xff0c;警察通过排查确定杀人凶手必为4个嫌疑犯的一个。 以下为4个嫌疑犯的供词: A说&#xff1a;不是我。 B说&#xff1a;是C。 C说&#xff1a;是D。 D说&#xff1a;C在胡说 已知3个人说了真话&#xff0c;1个人说的是假话。 现在请根据这…...

python-自动化篇-运维-实现读取日志文件最后一行的时间

文章目录 1. 使用Python打开日志文件2.python读取文件最后一行两种方式3.读取当前时间&#xff0c;进行两者相减&#xff0c;超时报警4.将内容推送到企业微信5. 关闭日志文件整体代码 1. 使用Python打开日志文件 在开始实时读取日志文件之前&#xff0c;我们首先需要打开一个日…...

QT SQL

QT SQL模块提供数据库编程的支持&#xff0c;支持多种常见的数据库&#xff1a;MySQL\Oracle\MS SQL Server\SQLite等。SQL模块包含多个类&#xff0c;可以实现&#xff1a;数据库连接、SQL语句执行、数据获取与界面显示 等功能。数据 与 界面间用Model\View架构。 一、 二、Q…...

C++(20):通过concept及nlohmann将数据转换为字符串

nlohmann可以自动兼容将C++的很多原生类型转换为json,甚至自定义类型也不需要太复杂的操作就可以转换为json,可以利用这一点将数据转换为string: #include <nlohmann/json.hpp> #include <string> #include <vector> #include <tuple> #include <…...

Transformer 自然语言处理(四)

原文&#xff1a;Natural Language Processing with Transformers 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第十章&#xff1a;从头开始训练变换器 在本书的开头段落中&#xff0c;我们提到了一个名为 GitHub Copilot 的复杂应用&#xff0c;它使用类似 GPT 的…...

BRAIN :帕金森病中与痴呆相关的动态功能连接改变

fMRI成像手段由于其在高空间分辨率的优势获得了疾病研究的青睐&#xff0c;越来越多的疾病研究使用fMRI手段来通过找到特异的神经标记物从而提升临床治疗的诊断效力以及准确率。但是&#xff0c;功能磁共振受到其时间分辨率相对较低这一缺点的影响&#xff0c;在对疾病时间特异…...

harmony os系统

因为实验室配的是Windows电脑&#xff0c;最近在搜索marginnote有没有windows的版本&#xff0c;不然好多功能相似的软件在使用不能信息同步是挺麻烦的。搜索结果当然还是没有对应版本。那我退而求其次&#xff0c;看看怎么在Windows上使用marginnote&#xff0c;结果大家意见基…...

2024美赛数学建模A题思路源码——七鳃鳗性别比例和生态系统关系

赛题目的:分析一个物种根据资源可用性改变其性别比例的能力的利弊。开发一个模型,分析对生态系统中由此产生的相互作用。 问题一.七鳃鳗性别比例对生态系统的影响 问题分析 建立一个简化版的模型,来探讨以下问题: 1.我们假设七鳃鳗种群的增长遵循Logistic生长模型,其中食…...

C语言的基础学习

C语言的变量 ## C语言中的变量 在C语言中,变量是对程序中数据所占内存空间的一种抽象定义。定义变量时,用户定义变量的名、变量的类型,这些都是变量的操作属性。不仅可以通过变量名访问该变量,系统还通过该标识符确定变量在内存中的位置 [❷](https://www.dotcpp.com/cour…...

PostGIS教程学习二十二:使用触发器追踪历史编辑操作

PostGIS教程学习二十二&#xff1a;使用触发器追踪历史编辑操作 生产环境下数据库的一个常见要求是能够跟踪用户编辑数据的历史&#xff1a;数据在两个日期之间是如何变化的&#xff0c;是谁操作的&#xff0c;以及它们哪些内容变化了&#xff1f;一些GIS系统通过在客户端接口…...

【PTA浙大版《C语言程序设计(第4版)》编程题】练习7-4 找出不是两个数组共有的元素(附测试点)

目录 输入格式: 输出格式: 输入样例: 输出样例: 代码呈现 测试点 给定两个整型数组&#xff0c;本题要求找出不是两者共有的元素。 输入格式: 输入分别在两行中给出两个整型数组&#xff0c;每行先给出正整数N&#xff08;≤20&#xff09;&#xff0c;随后是N个整数&a…...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API&#xff0c;用于在函数组件中使用 state 和其他 React 特性&#xff08;例如生命周期方法、context 等&#xff09;。Hooks 通过简洁的函数接口&#xff0c;解决了状态与 UI 的高度解耦&#xff0c;通过函数式编程范式实现更灵活 Rea…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

力扣热题100 k个一组反转链表题解

题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...

【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅

目录 前言 操作系统与驱动程序 是什么&#xff0c;为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中&#xff0c;我们在使用电子设备时&#xff0c;我们所输入执行的每一条指令最终大多都会作用到硬件上&#xff0c;比如下载一款软件最终会下载到硬盘上&am…...

【C++】纯虚函数类外可以写实现吗?

1. 答案 先说答案&#xff0c;可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...

k8s从入门到放弃之HPA控制器

k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率&#xff08;或其他自定义指标&#xff09;来调整这些对象的规模&#xff0c;从而帮助应用程序在负…...

区块链技术概述

区块链技术是一种去中心化、分布式账本技术&#xff0c;通过密码学、共识机制和智能合约等核心组件&#xff0c;实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点&#xff1a;数据存储在网络中的多个节点&#xff08;计算机&#xff09;&#xff0c;而非…...