Kotlin扩展函数与属性

扩展函数和属性是Kotlin中最具特色的功能之一,它们允许我们在不修改原有类的情况下为类添加新功能。这种设计既保持了类的封装性,又提供了强大的扩展能力。

一、扩展函数:为现有类添加新行为

1.1 基础扩展函数
扩展函数允许我们为任何类(包括Java类)添加新方法:

// 为String类添加扩展函数
fun String.addExclamation(): String {return "$this!"
}// 为Int添加平方计算
fun Int.square(): Int {return this * this
}fun main() {val greeting = "Hello"println(greeting.addExclamation()) // 输出: Hello!val number = 5println(number.square()) // 输出: 25
}

1.2 实用扩展函数示例
1.2.1 集合操作扩展

// 安全获取元素,越界时返回null
fun <T> List<T>.getSafe(index: Int): T? {return if (index in indices) get(index) else null
}// 统计满足条件的元素数量
fun <T> Iterable<T>.countBy(predicate: (T) -> Boolean): Int {return count(predicate)
}fun main() {val list = listOf("Apple", "Banana", "Cherry")println(list.getSafe(1)) // 输出: Bananaprintln(list.getSafe(5)) // 输出: nullval count = list.countBy { it.length > 5 }println("长度>5的单词数: $count") // 输出: 2
}

1.2.2 日期时间处理扩展

// 计算两个日期之间的天数
fun LocalDate.daysUntil(other: LocalDate): Long {return ChronoUnit.DAYS.between(this, other)
}// 判断是否是周末
fun LocalDate.isWeekend(): Boolean {return dayOfWeek.value in listOf(6, 7) // 6=周六, 7=周日
}fun main() {val today = LocalDate.now()val futureDate = today.plusDays(10)println("距离未来日期还有 ${today.daysUntil(futureDate)} 天")println("今天是周末吗? ${today.isWeekend()}")
}

1.3 扩展函数的高级用法
1.3.1 可空接收者扩展

// 为可空String添加扩展函数
fun String?.orEmpty(): String {return this ?: ""
}// 安全解析Int
fun String?.toIntOrZero(): Int {return this?.toIntOrNull() ?: 0
}fun main() {val nullString: String? = nullprintln(nullString.orEmpty().length) // 输出: 0val numString: String? = "42"println(numString.toIntOrZero()) // 输出: 42println(nullString.toIntOrZero()) // 输出: 0
}

1.3.2 泛型扩展函数

// 交换Map中的键值对
fun <K, V> Map<K, V>.invert(): Map<V, K> {return map { Pair(it.value, it.key) }.toMap()
}// 安全转换集合类型
fun <T, R> Collection<T>.convertAll(transform: (T) -> R): List<R> {return mapNotNull { runCatching { transform(it) }.getOrNull() }
}fun main() {val originalMap = mapOf("A" to 1, "B" to 2)println(originalMap.invert()) // 输出: {1=A, 2=B}val strings = listOf("1", "2", "three", "4")val numbers = strings.convertAll { it.toInt() }println(numbers) // 输出: [1, 2, 4]
}

二、扩展属性:为类添加新属性

扩展属性允许我们为现有类添加计算属性:
2.1 基础扩展属性

// 为String添加扩展属性:单词数
val String.wordCount: Intget() = split("\\s+".toRegex()).filter { it.isNotBlank() }.size// 为Int添加扩展属性:是否为偶数
val Int.isEven: Booleanget() = this % 2 == 0fun main() {val text = "Kotlin extensions are powerful"println("单词数: ${text.wordCount}") // 输出: 4println("10是偶数吗? ${10.isEven}") // 输出: trueprintln("7是偶数吗? ${7.isEven}")   // 输出: false
}

三、实战案例:构建DSL风格的验证库

// 验证结果类
data class ValidationResult(val isValid: Boolean,val errors: List<String> = emptyList()
) {operator fun plus(other: ValidationResult): ValidationResult {return ValidationResult(isValid = isValid && other.isValid,errors = errors + other.errors)}
}// 验证器扩展函数
fun String.validate(block: StringValidator.() -> Unit): ValidationResult {val validator = StringValidator(this)validator.block()return validator.result
}// 验证器类
class StringValidator(private val value: String) {private var result = ValidationResult(true)fun notEmpty(message: String = "不能为空") {if (value.isBlank()) {result = ValidationResult(false, result.errors + message)}}fun minLength(length: Int, message: String = "长度不足") {if (value.length < length) {result = ValidationResult(false, result.errors + "$message (最小$length)")}}fun maxLength(length: Int, message: String = "长度超限") {if (value.length > length) {result = ValidationResult(false, result.errors + "$message (最大$length)")}}fun matches(regex: Regex, message: String = "格式不匹配") {if (!value.matches(regex)) {result = ValidationResult(false, result.errors + message)}}
}// 使用示例
fun main() {val password = "secret123"val validation = password.validate {notEmpty()minLength(8)maxLength(20)matches(Regex(".*[0-9].*"), "必须包含数字")matches(Regex(".*[A-Z].*"), "必须包含大写字母") // 这个会失败}if (validation.isValid) {println("密码有效")} else {println("密码无效,原因:")validation.errors.forEach { println("- $it") }}/* 输出:密码无效,原因:- 必须包含大写字母*/
}

四、扩展函数与属性的最佳实践

4.1 设计原则
单一职责:每个扩展函数只做一件事
命名清晰:函数名应准确表达其功能
避免滥用:不是所有工具函数都适合做成扩展
文档完善:为非直观的扩展添加KDoc
4.2 实用技巧
4.2.1 链式调用设计

// 链式构建器风格
class StringBuilder {private val builder = StringBuilder()fun append(text: String): StringBuilder {builder.append(text)return this}fun appendLine(text: String = ""): StringBuilder {builder.appendLine(text)return this}override fun toString(): String = builder.toString()
}// 扩展函数支持链式调用
fun StringBuilder.bold(): StringBuilder {return append("<b>").append(this.toString()).append("</b>")
}fun main() {val result = StringBuilder().append("Hello").appendLine(", Kotlin!").appendLine("This is bold:").bold()println(result)
}

4.2 性能考虑
扩展函数是静态解析的,没有运行时开销
对于频繁调用的扩展,考虑内联(inline)优化
扩展属性有轻微性能开销(每次访问都会调用getter)

// 内联扩展函数示例
inline fun <T> Iterable<T>.sumByLong(selector: (T) -> Long): Long {var sum = 0Lfor (element in this) {sum += selector(element)}return sum
}

五、总结与进阶方向

5.1 核心要点回顾
扩展函数和属性是静态解析的,不会影响原有类的实例
扩展函数可以定义在顶层、类内部或对象中
扩展属性不能有幕后字段(backing field)
可空接收者扩展可以安全处理null值
5.2 扩展函数 vs 成员函数
特性	扩展函数	成员函数
定义位置	类外部	类内部
访问private成员	不能	能
虚函数(可重写)	不能	能
静态分发	是	是
构造函数	不能添加	能

5.3 进阶学习方向
探索Kotlin标准库中的常用扩展(如also, apply, let等)
研究扩展函数在Android开发中的应用(如View绑定)
学习如何为第三方库编写扩展函数
实践用扩展函数构建领域特定语言(DSL)

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

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

相关文章

厨师上门做饭小程序源码php方案

厨师上门做饭小程序源码&#xff0c;开发语言后端php&#xff0c;前端uniapp。可二开定制 三个端&#xff1a;用户端师傅端小程序&#xff0c;pc管理后台。 一 用户端 1.单点大厨&#xff1a;选择厨师预约下单&#xff0c;查看厨师评价、厨师的套餐。 2.点套餐&#xff1a;选择…

LLM大模型如何访问MySQL业务数据库

目录 一、LLM大模型如何访问MySQL业务数据库 1.1 为什么需要SQL Agent? 1.2 什么是 create_sql_agent&#xff1f; 1.3 什么是SQLDatabaseToolkit? 二、SQL Agent智能体操作MySQL数据库 三、本地启动服务 验证效果 四、怎么提高SQL Agent智能体的回复准确性&#xff1…

MySQL(112)如何选择读写分离策略?

选择读写分离策略是实施读写分离的关键一步。常见的读写分离策略包括简单的读写分离和基于负载均衡的读写分离。为了实现这些策略&#xff0c;我们需要动态地选择数据源。下面详细介绍如何实现基于Spring Boot的读写分离&#xff0c;并结合代码示例展示不同策略的实现。 读写分…

日志-解决Linux因target is busy无法卸载硬盘/分区的问题 - PHP持续占用分区

效果图 写在前面 此次遇到的问题是&#xff0c;php-fpm持续占用设备/mnt/disk1&#xff0c;强制杀死php所有进程后&#xff0c;依然会自动产生新的进程再次霸占分区&#xff0c;导致设备无法卸载umount。思路是解决谁在不停的捣乱。 步骤 核心&#xff1a; 挂载文件系统到指定…

Linux系统权限维持篇

Openssh后门 重新安装自定义的openssh&#xff0c;达到记录账户密码&#xff0c;也可以采用万能密码连接的功能 1、登录方式 2、登录软件 3、登录机制 环境准备 yum -y install openssl openssl-devel pam-devel zlib zlib-devel yum -y install gcc gcc-c makewget http://c…

spring中maven缺少包如何重新加载,报错java: 程序包org.springframework.web.reactive.function不存在

错误原因分析 java: 程序包org.springframework.web.reactive.function不存在 这个错误是由于 项目中缺少 Spring WebFlux 相关依赖 导致的。org.springframework.web.reactive.function 包属于 Spring WebFlux 模块&#xff08;用于响应式 Web 开发&#xff09;&#xff0c;如…

Linux--线程池

目录 Linux线程池 线程池的概念 线程池的优点 线程池的应用场景 线程池的实现 Linux线程池 线程池的概念 线程池是一种线程的使用模式。 其存在的主要原因就为&#xff1a;线程过多会带来调度开销&#xff0c;进而影响缓存局部性和整体性能。而线程池维护着多个线程&#…

mars3d (基于 Cesium 的轻量化三维地图库)

mars3d 是什么? Mars3D 作为基于 Cesium 的轻量化框架,正以其简洁的 API 和强大的功能重新定义开发体验。它不仅解决了原生 Cesium 学习曲线陡峭的问题,还通过封装和优化实现了性能与易用性的双重突破。无论是智慧城市、低空经济还是军事仿真,Mars3D 都能提供高效的三维可视…

uniapp 中使用路由导航守卫,进行登录鉴权

前言: 在uniapp 使用中,对于登录界面可能需要路由守卫进行方便判断跳转,以下有两种方案,可以判断用户跳转的时候是否是登录状态 方案一: 1. 可以使用插件 hh-router-guard 2. 使用 uni-simpe-route 方案二: 使用通过uni提供的拦截器实现, uni.addInterceptor 1.新建in…

Leetcode 262. 行程和用户

1.题目基本信息 1.1.题目描述 表&#xff1a;Trips ----------------------- | Column Name | Type | ----------------------- | id | int | | client_id | int | | driver_id | int | | city_id | int | | status | enum | | request_at | varchar | -----------…

P1102 A-B 数对

P1102 A-B 数对 题目背景 出题是一件痛苦的事情! 相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的 A+B Problem,改用 A-B 了哈哈! 题目描述 给出一串正整数数列以及一个正整数 C C C,要求计算出所有满足 A − B = C A - B = C A−B=C 的数对的个数(不同…

devextreme-vue的DxDataGrid如何显示行号列

devextreme-vue我使用的是23.2版本&#xff0c;其DxDataGrid如何显示行号列&#xff0c;官方一直没有方案。 DataGrid - How to display a row number in data rows in Angular | DevExpress Support dxDataGrid - provide capability to display a column with row numbers …

【设计模式06】建造者模式

前言 没什么用&#xff0c;类似于builder.build UML类图 代码示例 package com.sw.learn.pattern.B_create.e_builder;import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt();for …

datax-web报错:连接数据库失败. 请检查您的 账号、密码、数据库名称、IP、Port或者向 DBA 寻求帮助(注意网络环境)

文章目录 一、报错内容二、解决方法 一、报错内容 背景描述&#xff1a; 在linux安装了datax202309版本及datax-web2.1.2版本&#xff0c;datax与datax-web默认都是mysql5.x版本的。我的数据库是mysql8.x版本的。 在datax中执行json脚本从一个mysql导入mysql没问题&#xff0…

C#调用C++导出的dll怎么调试进入C++ DLL源码

第一步&#xff1a;首先需要打开C源码&#xff0c;不需要任何设置&#xff0c;直接下断点&#xff0c;然后将生成DLL目录改成到C# exe生成目录里面 第二步&#xff1a;打开winform项目&#xff0c;然后在C#项目属性->启用本地代码调试勾选后即可 最后在C#下断点F10或者F11…

Skyeye 云智能制造办公系统 - Saas v3.16.10 发布

Skyeye 云智能制造&#xff0c;采用 Springboot (微服务) Layui UNI-APP Ant Design Vue 的低代码平台。包含 30 多个应用模块、50 多种电子流程&#xff0c;CRM、PM、ERP、MES、ADM、EHR、笔记、知识库、项目、门店、商城、财务、多班次考勤、薪资、招聘、云售后、论坛、公…

pdf 合并 python实现(已解决)

在Python中&#xff0c;可以使用多种库来合并PDF文件&#xff0c;其中最常用的是PyPDF2和PyMuPDF&#xff08;又名fitz&#xff09;。下面我将分别介绍如何使用这两个库来合并PDF文件。 使用PyPDF2 首先&#xff0c;你需要安装PyPDF2。可以使用pip来安装&#xff1a; 先按照库…

VCenter SSL过期,登录提示HTTP 500错误解决办法

报错图&#xff1a; 1. 开启 VCenter ssh远程连接 登录vmware esxi&#xff0c;双击打开VCenter 控制台黑窗口&#xff0c;根据提示按F2键 两次&#xff0c;打开系统设置&#xff08;有fn键使用fnF2键&#xff09; 输入root密码&#xff0c;按回车登录 选择“Troubleshooting …

Linux 下安装Oracle 11gR2 x64 netca启动不了

前言 Oracle Network Configuration Assistant (netca) 是 Oracle 提供的图形化网络配置工具&#xff0c;用于简化 Oracle 数据库网络组件的配置和管理。 核心功能 1、配置监听器 (LISTENER)创建、修改或删除数据库监听器&#xff08;默认端口 1521&#xff09;定义监听协议…

Pytorch1线性代数实现

Pytorch --线性代数实现 矩阵 正如向量将标量从零阶推广到一阶&#xff0c;矩阵将向量从一阶推广到二阶。 矩阵&#xff0c;我们通常用粗体、大写字母来表示 &#xff08;例如&#xff0c;&#x1d44b;、&#x1d44c;和&#x1d44d;&#xff09;&#xff0c; 在代码中表示…