kotlin基础之泛型和委托
Kotlin泛型的概念及使用
泛型概念
在Kotlin中,泛型(Generics)是一种允许在类、接口和方法中使用类型参数的技术。这些类型参数在实例化类、实现接口或调用方法时会被具体的类型所替代。泛型的主要目的是提高代码的复用性、类型安全性和可读性。
泛型使用
- 泛型类
定义一个泛型类,可以在类名后面加上尖括号< >,并在其中声明类型参数。
class Box<T>(val item: T) { | |
fun getContent(): T { | |
return item | |
} | |
} | |
// 使用时指定类型参数 | |
val intBox = Box<Int>(10) | |
val stringBox = Box<String>("Hello") |
- 泛型函数
函数也可以有类型参数。
fun <T> printItems(items: List<T>) { | |
for (item in items) { | |
print(item) | |
print(", ") | |
} | |
println() | |
} | |
// 使用时,Kotlin会自动推断T的类型 | |
printItems(listOf(1, 2, 3)) | |
printItems(listOf("a", "b", "c")) |
- 泛型接口
与泛型类和泛型函数类似,接口也可以有类型参数。
interface Listener<T> { | |
fun onItemClicked(item: T) | |
} | |
// 实现泛型接口 | |
class ButtonClickListener<T> : Listener<T> { | |
override fun onItemClicked(item: T) { | |
// 处理点击事件 | |
} | |
} |
协变(Covariance)
协变是指在一个泛型类型中,如果类型参数是某个类的子类型,那么使用这个类型参数的泛型类型也应该是父类泛型类型的子类型。在Kotlin中,通过out修饰符实现协变。
interface Source<out T> { | |
fun next(): T? | |
} | |
fun demo(strs: Source<String>) { | |
// ... | |
} | |
val intSource: Source<Int> = ... | |
// 因为Int是String的子类型(在Kotlin中String不是Int的子类,这里仅作示例),但Source<Int>不是Source<String>的子类型 | |
// 所以不能直接传递intSource给demo函数,但可以通过协变实现 | |
demo(intSource as Source<String>) // 错误:类型不匹配 | |
// 正确的协变用法 | |
val stringSource: Source<out String> = intSource as? Source<out String> // 这里假设intSource实际上可以转换为Source<out String> | |
if (stringSource != null) { | |
demo(stringSource) // 正确 | |
} |
注意:在Kotlin中,String并不是Int的子类型,上面的例子仅用于说明协变的概念。
逆变(Contravariance)
逆变与协变相反,它指的是在一个泛型类型中,如果类型参数是某个类的父类型,那么使用这个类型参数的泛型类型也应该是子类泛型类型的父类型。在Kotlin中,通过in修饰符实现逆变。
interface Sink<in T> { | |
fun put(item: T) | |
} | |
fun fill(sink: Sink<Number>) { | |
// ... | |
} | |
val stringSink: Sink<String> = ... | |
// 因为String是Number的子类型,但Sink<String>不是Sink<Number>的子类型 | |
// 所以不能直接传递stringSink给fill函数,但可以通过逆变实现 | |
fill(stringSink as Sink<Number>) // 错误:类型不匹配 | |
// 正确的逆变用法 | |
val numberSink: Sink<in Number> = stringSink as? Sink<in Number> // 这里假设stringSink实际上可以转换为Sink<in Number> | |
if (numberSink != null) { | |
fill(numberSink) // 正确 | |
} |
同样,上面的例子仅用于说明逆变的概念,实际上String不是Number的子类型。
星号投射(Star Projection)
星号投射(*)在Kotlin中用于处理泛型类型的通配符情况。当你声明一个泛型类型但不想指定具体的类型参数时,可以使用星号投射。
使用方式:
-
协变星号投射:
List<out T*>通常简化为List<*>。这表示列表中的元素可以是任何类型,但当你从列表中取出元素时,它的类型会被视为Any?(因为任何类型都可以赋值给Any?)。
val list: List<*> = ... // list可以是任何类型的List | |
for (item in list) { | |
if (item is String) { | |
println(item.length) // 只有在确定item是String类型时才能调用其方法 | |
} | |
} |
- 逆变星号投射:在Kotlin中,逆变星号投射不常用,因为Kotlin的泛型系统主要基于协变和不变。但在某些高级用法中,你可能会遇到类似于
Sink<in T*>的逆变星号投射,这表示该接口或类可以接受任何类型的参数。
委托(Delegation)
概念:
委托(Delegation)是一种设计模式,它允许一个对象(委托对象)将其职责的一部分或全部委托给另一个对象(被委托对象)。委托模式可以提高代码的复用性和可维护性。
使用:
-
类委托:在Kotlin中,可以使用
by关键字来实现类委托。这允许一个类将某些方法的实现委托给另一个类的实例。
class Base { | |
fun printMessage() { | |
println("Message from Base") | |
} | |
} | |
class Derived(b: Base) : Base() by b { | |
// Derived类将Base类的printMessage方法委托给b实例 | |
} | |
fun main() { | |
val derived = Derived(Base()) | |
derived.printMessage() // 输出 "Message from Base" | |
} |
注意:在上面的例子中,Derived类继承了Base类,但实际上并没有重写printMessage方法。相反,它使用by关键字将该方法的调用委托给了b实例(即Base类的一个实例)。
2. 属性委托:Kotlin还支持属性委托,允许你将属性的get和set操作委托给另一个对象或表达式。这可以通过在属性声明中使用by关键字和相应的委托提供程序来实现。
class LazyValue<T>(private val initializer: () -> T) { | |
private var value: T? = null | |
fun getValue(): T { | |
if (value == null) { | |
value = initializer() | |
} | |
return value!! | |
} | |
// 这里省略了setValue方法,因为我们只关心只读属性 | |
} | |
class Example { | |
val lazyString: String by LazyValue { "Hello, World!" } | |
} | |
fun main() { | |
val example = Example() | |
println(example.lazyString) // 输出 "Hello, World!",并且只会在第一次访问时计算值 | |
} |
在这个例子中,lazyString属性的get操作被委托给了LazyValue类的实例。当第一次访问lazyString时,它会调用LazyValue的getValue方法来计算并缓存值。之后的访问将直接返回缓存的值。
相关文章:
kotlin基础之泛型和委托
Kotlin泛型的概念及使用 泛型概念 在Kotlin中,泛型(Generics)是一种允许在类、接口和方法中使用类型参数的技术。这些类型参数在实例化类、实现接口或调用方法时会被具体的类型所替代。泛型的主要目的是提高代码的复用性、类型安全性和可读…...
awtk踩坑记录二:移植jerryscript到awtk design项目
工作要求,想尝试看看在awtk-designer设计界面的同时能不能用javascript开发逻辑层,以此和前端技术联动,本文是一种项目建构的思路。 从github下载并编译awtk, awtk-mmvm和awtk-jerryscript(如果没有) 用awtk-designer…...
正邦科技(day2)
自动校准 问题:电量不准都可以直接去校准 校准方式:可程式变频电压 问题分析:他是通过软件去自动自动校准的,flash 清空的时候有缓存没有清空,或者互感器没有读取到问题 互感器:电流互感器的作用包括电流测…...
技术架构设计指南:从需求到实现
技术架构是软件系统的骨架,它决定了系统的性能、可靠性、扩展性等关键特性。本文将介绍技术架构设计的一般步骤和方法。 第一步:需求分析 在设计技术架构之前,首先要对系统需求进行全面深入的分析。这包括功能需求、非功能需求(如…...
【数据结构:排序算法】堆排序(图文详解)
🎁个人主页:我们的五年 🔍系列专栏:数据结构课程学习 🎉欢迎大家点赞👍评论📝收藏⭐文章 目录 🍩1.大堆和小堆 🍩2.向上调整算法建堆和向下调整算法建堆:…...
git 派生仓库怎么同步主仓库的新分支
一、git 派生仓库怎么同步主仓库的新分支 要使你的Git派生仓库同步主仓库的新分支,请遵循以下步骤: 1、添加上游仓库(如果尚未添加): 如之前所述,确保上游仓库已经被添加到你的本地仓库。如果没有,使用命…...
对比方案:5款知识中台工具的优缺点详解
知识中台工具为企业和组织高效地组织、存储和分享知识,还能提升团队协作的效率。在选择搭建知识中台的工具时,了解工具的优缺点,有助于企业做出最佳决策。本文LookLook同学将对五款搭建知识中台的工具进行优缺点的简单介绍,帮助企…...
第16章-超声波跟随功能 基于STM32的三路超声波自动跟随小车 毕业设计 课程设计
第16章-超声波跟随功能 无PID跟随功能 //超声波跟随if(HC_SR04_Read() > 25){motorForward();//前进HAL_Delay(100);}if(HC_SR04_Read() < 20){motorBackward();//后退HAL_Delay(100);}PID跟随功能 在pid.c中定义一组PID参数 tPid pidFollow; //定距离跟随PIDpidFol…...
创新案例 | 持续增长,好孩子集团的全球化品牌矩阵战略与客户中心设计哲学
探索好孩子集团如何通过创新设计的全球化品牌矩阵和以客户为中心的产品策略,在竞争激烈的母婴市场中实现持续增长。深入了解其品牌价值观、市场定位策略以及如何满足新一代父母的需求。本文旨在为中高级职场人士、创业家及创新精英提供深度见解,帮助他们…...
ResNet 原理剖析以及代码复现
原理 ResNet 解决了什么问题? 一言以蔽之:解决了深度的神经网络难以训练的问题。 具体的说,理论上神经网络的深度越深,其训练效果应该越好,但实际上并非如此,层数越深会导致越差的结果并且容易产生梯度爆炸…...
数据结构(十)图
文章目录 图的简介图的定义图的结构图的分类无向图有向图带权图(Wighted Graph) 图的存储邻接矩阵(Adjacency Matrix)邻接表代码实现 图的遍历深度优先搜索(DFS,Depth Fisrt Search)遍历抖索过程…...
四数之和-力扣
本题在三数之和的基础上,再增加一重循环进行解答 首先注意的点是,一级剪枝处理,target > 0 && nums[i] > target 此处只有整数才可剪枝处理,如果target为负数,nums[i] < target,也不能代…...
JS 中怎么删除数组元素?有哪几种方法?
正文开始之前推荐一位宝藏博主免费分享的学习教程,学起来! 编号学习链接1Cesium: 保姆级教程+源码示例2openlayers: 保姆级教程+源码示例3Leaflet: 保姆级教程+源码示例4MapboxGL: 保姆级教程+源码示例splice() JavaScript中的splice()方法是一个内置的数组对象函数, 用于…...
Git如何将pre-commit也提交到仓库
我一开始准备将pre-commit提交到仓库进行备份的,但是却发现提交不了,即使我使用强制提交都不行。 (main) $ git add ./.git/hooks/pre-commit(main) $ git status On branch main nothing to commit, working tree clean# 强制提交(main) $ git add -f .…...
vmware中Ubuntu虚拟机和本地电脑Win10互相ping通
初始状态 使用vmware17版本安装的Ubuntu的20版本,安装之后什么配置都要不懂,然后进行下述配置。 初始的时候是NAT,没动的. 设置 点击右键编辑“属性” 常规选择“启用”: 高级选择全部: 打开网络配置,右键属…...
比较含退格的字符串-力扣
做这道题时出现了许多问题 第一次做题思路是使用双指针去解决,快慢指针遇到字母则前进,遇到 # 则慢指针退1,最开始并未考虑到 slowindex < 0 ,从而导致越界。第二个问题在于,在最后判断两个字符串是否相同时,最初使…...
NSSCTF-Web题目4
[SWPUCTF 2021 新生赛]hardrce 1、题目 2、知识点 rce:远程代码执行、url取反编码 3、解题思路 打开题目 出现一段代码,审计源代码 题目需要我们通过get方式输入变量wllm的值 但是变量的值被过滤了,不能输入字母和\t、\n等值 所以我们需…...
7. CSS 网格布局
CSS3引入了强大的网格布局(Grid Layout),它提供了一种二维的布局方式,使得创建复杂的网页布局变得更加简单和直观。通过定义行和列,我们可以精确控制网页元素的排列和对齐。本章将详细介绍网格布局的基本概念和属性&am…...
如何配置才能连接远程服务器上的 redis server ?
文章目录 Intro修改点 Intro 以阿里云服为例。 首先,我在我买的阿里云服务器中以下载源码、手动编译的方式安装了 redis-server,操作流程见:Ubuntu redis 下载解压配置使用及密码管理 && 包管理工具联网安装。 接着,我…...
MindSpore实践图神经网络之环境篇
MindSpore在Windows11系统下的环境配置。 MindSpore环境配置大概分为三步:(1)安装Python环境,(2)安装MindSpore,(3)验证是否成功 如果是GPU环境还需安装CUDA等环境&…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
