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 —— 必须和属性同类型或者是它的超类型。

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

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

相关文章

2024美赛数学建模A题思路分析 - 资源可用性和性别比例

1 赛题 问题A&#xff1a;资源可用性和性别比例 虽然一些动物物种存在于通常的雄性或雌性性别之外&#xff0c;但大多数物种实质上是雄性或雌性。虽然许多物种在出生时的性别比例为1&#xff1a;1&#xff0c;但其他物种的性别比例并不均匀。这被称为适应性性别比例的变化。例…

【Javaweb程序】【C00155】基于SSM的旅游旅行管理系统(论文+PPT)

基于SSM的旅游旅行管理系统&#xff08;论文PPT&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于SSM的旅游旅行管理系统 本系统分为前台系统模块、管理员模块、用户模块以及商家模块 其中前台系统模块的权限为&#xff1a;当游客打开系统的网址后…

C++ 数论相关题目 博弈论:拆分-Nim游戏

给定 n 堆石子&#xff0c;两位玩家轮流操作&#xff0c;每次操作可以取走其中的一堆石子&#xff0c;然后放入两堆规模更小的石子&#xff08;新堆规模可以为 0 &#xff0c;且两个新堆的石子总数可以大于取走的那堆石子数&#xff09;&#xff0c;最后无法进行操作的人视为失…

Scrum敏捷开发企业培训-敏捷研发管理

课程简介 Scrum是目前运用最为广泛的敏捷开发方法&#xff0c;是一个轻量级的项目管理和产品研发管理框架。 这是一个两天的实训课程&#xff0c;面向研发管理者、项目经理、产品经理、研发团队等&#xff0c;旨在帮助学员全面系统地学习Scrum和敏捷开发, 帮助企业快速启动敏…

带libc源码gdb动态调试(导入glibc库使得可执行文件动态调试时可看见调用库函数源码)

文章目录 参考部分查看源码是否编译时有-g调试信息和符号表在 gdb 中加载 debug 文件/符号表将 debug 文件放入 ".debug" 文件夹通过 gdb 命令 set debug-file-directory directories GCC的gcc和g区别指定gcc/g&#xff0c;glibc的版本进行编译指定gcc/g的版本指定gl…

bash脚本学习笔记

一、扫盲 脚本文件是一种文本文件&#xff0c;其中包含了一系列的命令和指令&#xff0c;可以被操作系统解释器直接解释执行。脚本文件通常被用来完成特定的任务或执行重复性的操作。 脚本文件通常以某种编程语言的语法编写&#xff0c;例如 Bash、Python、Perl、Ruby 等等。…

Vue Router

Vue Router 一、Vue Router 回顾 1、路由简介 路由是一个比较广义和抽象的概念&#xff0c;路由的本质就是对应关系。 在开发中&#xff0c;路由分为&#xff1a; ​ 后端路由​ 前端路由 后端路由 概念&#xff1a;根据不同的用户 URL 请求&#xff0c;返回不同的内容本…

k8s中调整Pod数量限制的方法

一、介绍 Kubernetes节点每个默认允许最多创建110个pod&#xff0c;有时可能由于主机配置扩容的问题&#xff0c;从而需要修改节点pod运行数量的限制。 即&#xff1a;需要调整Node节点的最大可运行Pod数量。 一般来说&#xff0c;只需要在kubelet启动命令中增加–max-pods参数…

MySQL进阶之锁(全局锁以及备份报错解决)

锁 全局锁 全局锁就是对整个数据库实例加锁&#xff0c;加锁后整个实例就处于只读状态&#xff0c;后续的DML的写语句&#xff0c;DDL语 句&#xff0c;已经更新操作的事务提交语句都将被阻塞。 其典型的使用场景是做全库的逻辑备份&#xff0c;对所有的表进行锁定&#xff…

Java 基于 SpringBoot+Vue 的考研论坛管理系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

在PostgreSQL中不开归档?恭喜你!锅你背定了

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

微信小程序~上推加载更多组件

本组件使用的是TaroReact 实现的 &#xff0c;具体代码如下 一共分为tsx和less文件 //index.tsx /** RefreshLoading* description 上推加载更多组件* param loading boolean* param style* returns*/import { View } from "tarojs/components"; import React, { FC…

深入解剖指针篇(3)

个人主页&#xff08;找往期文章&#xff09; &#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 目录 二级指针 指针数组 指针数组模拟二维数组 字符指针变量 数组指针 数组指针初始化 二维数组传参的本质 函数指针 函数指针的使用 typedef关键字 函数指针数组 二级指针…

U盘文件管理,禁止拷贝文件到U盘的解决办法

在许多企业和组织中&#xff0c;为了防止敏感数据的泄露和保护计算机系统的安全&#xff0c;通常会采取一些措施来限制员工拷贝文件到U盘的行为。然而&#xff0c;有些员工可能会试图绕过这些限制&#xff0c;导致数据安全风险增加。 案例 2015年5月&#xff0c;隶属于某县政府…

【yaml 文件使用】pytest+request 框架中 yaml 配置文件使用

又来进步一点点~~ 背景&#xff1a;最近在学习pytestrequest框架写接口测试自动化&#xff0c;使用yaml文件配置更方便管理用例中的数据&#xff0c;这样更方便 yaml 介绍&#xff1a; 什么是 yaml 文件&#xff1a;YAML 是 “YAML Ain’t a Markup Language”&#xff08;Y…

Android 系统启动流程

依旧是带着问题再去学习 首先&#xff0c;Android是怎么启动的&#xff1f; Android服务是怎么启动的&#xff1f; Android线程是怎么切换的&#xff1f; Android ApplicationThread是怎么创建的&#xff1f; 那么接下来开始分析Android的启动流程 还是一步一图 先画一张流…

jenkins pipeline配置maven可选参数

1、在Manage Jenkins下的Global Tool Configuration下对应的maven项添加我们要用得到的不同版本的maven安装项 2、pipeline文件内容具体如下 我们maven是单一的&#xff0c;所以我们都是配置单选参数 pipeline {agent anyparameters {gitParameter(name: BRANCH_TAG, type: …

Jenkins手把手图文教程[基于Jenkins 2.164.1]

Jenkins手把手图文教程[基于Jenkins 2.164.1] 本文基于当前最新的 LTS 版本 2.164.1 &#xff0c;理论上适应于Jenkins 2.x所有版本。 一、下载 前往https://jenkins.io/download/ &#xff0c;按需下载。如用于生产&#xff0c;建议下载Long-term Support (LTS) 版本&#xf…

【全网最全】2024美赛ABCDEF题思路模型全解(后续会更新)

欲获取更多资料&#xff0c;一定要点击这里并关注文末的公众号&#xff01;&#xff01;&#xff01; 最新更新&#xff1a;我们团队不仅在第一时间更新了24美赛全题目的深度翻译和深入分析&#xff0c;经过爆肝奋战&#xff0c;我们在第一时间给出了ABCDEF全题目的完整建模过程…