Kotlin新手教程七(委托)

news2024/11/22 22:34:17

委托模式是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。kotlin中使用by实现委托。

一、类委托

类的委托实际就是一个类中定义的方法实际是调用另一个类中的对象的方法来实现的。看以下例子:

//定义一个Base()接口
interface Base {
    fun print()
}
//BaseImpl实现Base接口并重写print方法
class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}
//委托
class Derived(b: Base) : Base by b

fun main() {
    val b = BaseImpl(10)
    Derived(b).print()
}

Derived 的超类型列表中的 by-子句表示 b 将会在 Derived 中内部存储, 并且编译器将生成转发给 b 的所有 Base 的方法。

1.覆盖由委托实现的接口成员

编译器会使用 override 覆盖的实现而不是委托对象中的。比如上述的例子,如果Derived类重写了print方法的话,那么调用print方法就会调用的是Derived中的print方法,而不是BaseImpl中的

interface Base {
    fun print()
}
//BaseImpl实现Base接口并重写print方法
class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}
//委托
class Derived(b: Base) : Base by b{
    override fun print() {
        println("Derived")
    }
}

fun main() {
    val b = BaseImpl(10)
    Derived(b).print()
}

输出结果为:
在这里插入图片描述
但以这种方式重写的成员不会在委托对象的成员中调用 ,委托对象的成员只能访问其自身对接口成员实现:

interface Base {
    val message: String
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override val message = "BaseImpl: x = $x"
    override fun print() { println(message) }
}

class Derived(b: Base) : Base by b {
    // 在 b 的 `print` 实现中不会访问到这个属性
    override val message = "Message of Derived"
}

fun main() {
    val b = BaseImpl(10)
    val derived = Derived(b)
    derived.print()
    println(derived.message)
}

二、属性委托

属性委托简单来说就是一个类中的属性不直接在这个类中直接定义,而是交给另外一个类,从而方便管理。
属性委托最常用的几种情况为:

  1. 延迟属性:其值只在首次访问时计算
  2. 可观察属性:监听器会收到有关此属性变更的通知
  3. 多个属性储存在一个映射(Map)中,而不是每个存在单独的字段中

1.属性委托语法

val/var <属性名> :<类型> by <表达式>,表达式也就是想要委托的对象,若声明的属性时val修饰,则需要委托对象提供一个getValue方法,若是var,则需要提供getValue和setValue方法:

import kotlin.reflect.KProperty

class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${property.name}' to me!"
    }
 
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("$value has been assigned to '${property.name}' in $thisRef.")
    }
}

2.延迟属性Lazy

我们可以使用lazy()实现延迟属性,lazy()是当第一次调用get,也就是你第一次使用时会执行lazy中的表达式,但是第一次以后就只会返回结果。

val str:String by lazy {
    println("First")
    "str"
}

fun main() {
    println(str)
    println(str)
}

运行结果:
在这里插入图片描述

默认情况下,对于 lazy 属性的求值是同步锁的(synchronized):该值只在一个线程中计算,并且所有线程会看到相同的值。如果初始化委托的同步锁不是必需的,这样多个线程可以同时执行,那么将 LazyThreadSafetyMode.PUBLICATION 作为参数传递给 lazy() 函数。 而如果你确定初始化将总是发生在与属性使用位于相同的线程, 那么可以使用 LazyThreadSafetyMode.NONE 模式:它不会有任何线程安全的保证以及相关的开销。

3.可观察属性 Observable

Delegates.observable() 接受两个参数:初始值与修改时处理程序(handler)。 每当我们给属性赋值时会调用该处理程序(在赋值后执行)。它有三个参数:被赋值的属性、旧值与新值:

import kotlin.properties.Delegates

class User {
    var name: String by Delegates.observable("<no name>") {
        prop, old, new ->
        println("$old -> $new")
    }
}

fun main() {
    val user = User()
    user.name = "first"
    user.name = "second"
}

运行结果:
在这里插入图片描述

4.委托给另一个属性

从1.4版本往后一个属性可以将其getter和setter委托给另一个属性。使用::限定符进行委托:

var MyClass.extDelegated: Int by ::topLevelInt

用法:当想要以一种向后兼容的方式重命名一个属性的时候:引入一个新的属性、 使用 @Deprecated 注解来注解旧的属性、并委托其实现。

class MyClass {
   var newName: Int = 0
   @Deprecated("Use 'newName' instead", ReplaceWith("newName"))
   var oldName: Int by this::newName
}

fun main() {
   val myClass = MyClass()
   // 通知:'oldName: Int' is deprecated.
   // Use 'newName' instead
   myClass.oldName = 42
   println(myClass.newName) // 42
}

5.将属性存储在映射中

我们还可以通过将属性映射在Map中实现属性委托:

class User(val map: Map<String, Any?>) {
    val name: String by map
    val age: Int     by map
}
val user = User(mapOf(
    "name" to "John Doe",
    "age"  to 25
))
println(user.name) // Prints "John Doe"
println(user.age)  // Prints 25

上一篇:Kotlin新手教程六(数据类、密封类、枚举类、泛型)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

5.7 BGP属-ORIGIN

配置BGP ORIGIN属性控制选路 1. 实验目的 熟悉BGP ORIGIN属性控制选路的应用场景掌握BGP ORIGIN属性控制选路的配置方法2. 实验拓扑 实验拓扑如图5-7所示: 图5-7:BGP ORIGIN属性控制选路 3. 实验步骤 (1)配置IP地址 …

实验室通风橱通风柜的构成

一、实验室通风橱通风柜简介通风柜是一个密闭的同时又能排风的工作空间。其设计目的是为了控制、稀释以及排除这个密闭空间内产生制造的烟气、气雾和微粒&#xff0c;同时它也是实验室预防泄露控制的重要组成部分。在大多数实验室中&#xff0c;通风柜是保护实验室操作者免受有…

ubantu python完整安装示例(python3.7.1演示)

文章目录前言准备源码包1.下载2.解压准备工作&#xff08;重要&#xff09;1.下载cmake(用于编译源码&#xff09;2.下载必要的Module注意事项编译安装链接并验证配置环境变量1.移除原3.5link2.更换默认python3 的版本为3.73.添加路径前言 为什么需要使用源码编译安装&#xf…

分享项目 - Vue3 + TS + element-ui-plus 项目 -- Table表格表单

文章目录前言项目地址以及怎么阅读别人的代码整体代码分页数据作者是怎么处理的 usePagination顺藤摸瓜找到 api 接口的封装api 接口再往底层找全局请求封装与请求拦截器 service.ts前言 今天看一个 ts 项目的 table 模块&#xff0c;亲身体验这是公司后台管理系统一定会使用到…

Springboot @Test 给Controller接口 写 单元测试

前言 最近有小伙伴问到怎么给 controller的接口写单元测试。 单元测试是开发必不可少的一个环节。 既然有人问到了&#xff0c;那我觉得可能不止一个人不会&#xff0c;那就按照惯例&#xff0c;出手。 正文 内容&#xff1a; 主要是get 和 post 两种请求方式的接口 的 单元测…

centos7 开机自启动自定义脚本

centos7 开机自启动自定义脚本背景配置自启动jar1.首先书写自启动脚本2.在rc.local中加入脚本reboot测试docker版本的自启动背景 项目中有遇到2个问题&#xff0c; 1&#xff1a; 使用java启动jar包 2&#xff1a; docker容器中自启动个服务。 这2个都要使用linux的开机自启动问…

AcWing 1017. 怪盗基德的滑翔翼

怪盗基德是一个充满传奇色彩的怪盗&#xff0c;专门以珠宝为目标的超级盗窃犯。而他最为突出的地方&#xff0c;就是他每次都能逃脱中村警部的重重围堵&#xff0c;而这也很大程度上是多亏了他随身携带的便于操作的滑翔翼。有一天&#xff0c;怪盗基德像往常一样偷走了一颗珍贵…

【图像分类】卷积神经网络之LeNet5网络模型实现MNIST手写数字识别

写在前面: 首先感谢兄弟们的关注和订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 在上一篇博文中我们对LeNet5网络模型的结构进行了剖析,本篇博文,我们将使用PyTorch搭建LeNet5实现MNIST手写数字…

使用vmware制作云平台redhat7.9镜像模板

一、概述 1.1 redhat7.9 定制镜像上传到云平台。 这个制作镜像得方式适用于多种iso 镜像。 将iso 镜像通过vmware 创建出一台虚机&#xff0c;对虚机做一些基础配置。在虚机上安装kvm 虚拟化得工具&#xff0c; 将iso 镜像在导入虚机种通过kvm创建一下虚机&#xff0c; 虚机创…

Embedding 理解

Word Embedding 单词表示最简单的是 one-hot 但是它的缺点是 矩阵表示过于稀疏&#xff0c;占用空间对相关的词语无法得知它们的含义是相近的。 Word Embedding 解决了上述两个缺点&#xff0c;一个 Word Embedding 直观的例子如下图所示。 每个维度表示一个特征&#xff0…

简述操作系统的文件系统

前言 文件系统是操作系统中负责管理持久数据的子系统&#xff0c;将用户的文件保存在硬盘等硬件设备中&#xff0c;即使断电了数据也不会丢失。 对于用户而言&#xff0c;文件是存储的最小单位&#xff0c;再少的数据也需要以文件的形式存储在外部存储器中。以硬盘为例&#…

金三银四,我不允许你们不知道这些软件测试面试题

01、您所熟悉的测试用例设计方法都有哪些&#xff1f;请分别以具体的例子来说明这些方法在测试用例设计工作中的应用。 答&#xff1a;有黑盒和白盒两种测试种类&#xff0c;黑盒有等价类划分法&#xff0c;边界分析法&#xff0c;因果图法和错误猜测法。白盒有逻辑覆盖法&…

DataX及DataX-Web

大数据Hadoop之——数据同步工具DataX数据采集工具-DataX datax详细介绍及使用 一、概述 DataX 是阿里云DataWorks数据集成的开源版本&#xff0c;在阿里巴巴集团内被广泛使用的离线数据同步工具/平台。DataX 实现了包括 MySQL、Oracle、OceanBase、SqlServer、Postgre、HDFS、…

前端将base64图片转换成file文件

1、base64转成file具体代码 // base64图片转file的方法&#xff08;base64图片, 设置生成file的文件名&#xff09;function base64ToFile(base64, fileName) {// 将base64按照 , 进行分割 将前缀 与后续内容分隔开let data base64.split(,);// 利用正则表达式 从前缀中获取图…

PAT 甲级 1002 python 测试点1未通过

题目&#xff1a; 思路&#xff1a; 1注意多项式非零项的数目 2两项相加为0时不输出 3 测试点1未通过 代码&#xff1a; C{} Alist(input().split()) Blist(input().split())count0 for i in range(int(A[0])):C[A[i*21]]float(A[i*22])count count 1for i in range(int(B…

支付宝支付功能使用

1、进入“蚂蚁金服开放平台” https://open.alipay.com/https://open.alipay.com/ 2、下载支付宝官方 demo&#xff0c;进行配置和测试 文档地址 手机网站支付 DEMO &#xff5c; 网页&移动应用支付宝文档中心https://opendocs.alipay.com/open/02no47 demo下载 网页…

如何使用ngxin的 upstream

1.引言&#xff1a; 1.1反向代理&#xff1a; 反向代理是充当Web服务器网关的代理服务器。当您将请求发送到使用反向代理的Web服务器时&#xff0c;他们将先转到反向代理&#xff0c;由该代理将确定是将其路由到Web服务器还是将其阻止。 这意味着有了反向代理&#xff0c;您…

Java Stream流1

目录1 Stream流1.1 Stream流的生成方式1.2 Stream流中间操作之 filter1.3 Stream流中间操作之 limit 和 skip1.4 Stream流中间操作之 concat 和 distinct1 Stream流 体验Stream流需求 package test;import java.lang.reflect.Array; import java.util.ArrayList;public clas…

Arduino IDE搭建Heltec开发板开发环境

Arduino IDE搭建Heltec开发板开发环境Heltec开发板开发环境下载与搭建Arduino IDE下载与安装搭建Heltec开发板的开发环境添加package URL方法通过Git的方法安装离线安装Heltec开发板开发环境下载与搭建 Arduino IDE下载与安装 Heltec的ESP系列和大部分的LoRa系列开发板都是用A…

2.Gin内容介绍

目录 参考 主要内容 关于Web 创建项目 为什么要用框架 Gin框架介绍 Gin框架安装与使用 安装 第一个Gin示例&#xff1a; RESTful API Gin渲染 HTML渲染 自定义模板函数 静态文件处理 使用模板继承 补充文件路径处理 JSON渲染 XML渲染 YMAL渲染 protobuf渲染…