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等环境&…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...

关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...

前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...