当前位置: 首页 > news >正文

Kotion 常见用法注意事项(持续更新...)

1 简介

本文并非一份系统性的编程教程,而是在学习和项目开发工程中的一些感悟或经验总结。可能你和我会有不同的感受,毕竟每个人踩过的坑不一样,走的路也不尽相同,权当是一位 Kotlin 开发者的心得闲谈吧。

2 内联高阶函数

在 Kotlin 中,高阶函数允许将函数作为参数传递或返回函数。这种灵活性带来了一些便利,但也可能导致性能开销,特别是在频繁调用高阶函数时,因为每次调用时都会创建函数对象。如果你希望避免这种开销,可以使用 inline 关键字来标记高阶函数。

使用 inline 关键字时,编译器会在调用高阶函数的地方直接插入对应的代码,而不是生成一个函数调用,从而减少了对象创建的开销。下面是实现 inline 函数的步骤和示例。

实现方式

// 定义一个inline高阶函数
inline fun performOperation(operation: () -> Unit) {// 在函数体内可以执行某些操作println("Before operation.")operation()  // 调用传入的函数println("After operation.")
}// 使用inline函数
fun main() {performOperation {println("Operation is performed!")}
}

在没有使用 inline 的情况下,调用 performOperation 函数时,会创建一个函数对象,并传递这个对象(可能导致额外的内存开销)。而使用 inline 声明的函数会在编译时将其调用位置替换为函数体,这样就避免了不必要的对象创建,提高了性能,尤其是在频繁调用高阶函数时。

注意点

  • 递归调用 :不能将递归函数标记为 inline,因为这会导致重复的代码插入,增加方法体的大小。
  • 代码大小 :如果一个 inline函数的体积很大且被多次调用,最终生成的代码可能会增大 APK 的大小,需谨慎使用。
  • Lambda 语义 :虽然 inline函数会将代码嵌入到调用者的位置,但 lambda 表达式的闭包特性仍然保留,可以继承外部变量的值。

3 善用类型别名

如果函数/类签名冗长且重复,使用类型别名来简化它们:

typealias Callback = (Boolean, String?) -> Unit // 简化冗长的 lambda 表达式
fun loginUser(username: String, password: String, callback: Callback) {// ...
}typealias UserOrderMap = Map<String, OrderList> // 简化冗长的泛型集合

这会使你的 API 更加简洁,更易于阅读。

4 慎用协程的全局作用域

谨慎做法:

GlobalScope.launch { fetchData() }

推荐做法:

viewModelScope.launch { fetchData() }

通过 viewModelScope、lifecycleScope 或自定义的 CoroutineScope 使用结构化并发,可确保生命周期管理协程并防止泄漏。

5 reified 泛型函数

在 Kotlin 中,reified 关键字用于与 inline 函数结合,以允许在运行时访问类型参数的具体类型。
当你在 Kotlin 的 inline 函数中使用 reified 类型参数时,能够在函数中直接访问该类型的具体类型信息。这提供了更多灵活性,特别是当你希望基于类型执行某些操作时,例如类型检查或实例化。

inline fun <reified T> Gson.fromJson(json: String): T =this.fromJson(json, T::class.java)
// Usage
val user: User = gson.fromJson(jsonString)

比手动传递 Class 更简洁、更安全。

6 函数式编程的惯用手法

Kotlin 通过 map、filter、flatMap、reduce 等函数鼓励使用函数式编程:

val evenSquares = (1..10).filter { it % 2 == 0 }.map { it * it }

与手动循环相比,这会使代码更加简洁且富有表现力。

在集合上链式调用多个操作(如 map + filter + flatMap)时,会产生中间集合,带来额外内存开销。若操作较复杂或数据量较大,推荐使用 asSequence() 转为惰性序列,以减少中间产物,提升性能:

listOf(1,2,3,4,5).asSequence().map { it * 3  }.filter {  it > 2 }.flatMap { (0..it) }.toList()

实现原理

  • 惰性计算 : 使用 asSequence() 后,集合会被转换为一个 Sequence。在 Sequence 上进行的操作(如
    map、filter 等)不会立即计算,而是返回一个新的 Sequence,该序列只有在实际消费(例如通过
    toList()、forEach() 等)时,才会对数据进行真正的计算。 链式操作的优化 : 当多个操作链式调用时,Kotlin
    会将这些操作合并成一个操作链。在处理数据时,只会遍历原始集合一次,而不是为每个链式操作生成一个中间集合。这显著减少了内存开销和不必要的计算。
    终端操作 : 只有当执行终端操作(如
    toList()、toSet()、forEach()等)时,序列中的元素才会被计算和收集。这意味着在执行这些终端操作之前,任何转换或过滤都会在一个统一的过程内完成。

代码示例

以下是一个使用 asSequence() 的简单示例,演示如何减少内存开销:

fun main() {// 创建一个大集合val numbers = (1..1_000_000).toList()// 使用链式调用,产生多个中间集合val resultWithLists = numbers.filter { it % 2 == 0 }   // 筛选出偶数.map { it * it }          // 计算平方// 通过 asSequence() 进行惰性计算val resultWithSequence = numbers.asSequence().filter { it % 2 == 0 }   // 筛选出偶数.map { it * it }          // 计算平方.toList()                 // 终端操作,触发计算// 输出结果长度println("Result with lists size: ${resultWithLists.size}")println("Result with sequence size: ${resultWithSequence.size}")
}

优势

  • 内存效率 :
    使用 asSequence() 可以显著减少内存开销,特别是在数据量很大时,因为它避免了多个中间集合的创建。
  • 性能提升 :
    惰性处理使得链式调用的计算过程更高效。在使用惰性序列时,整个计算过程是在一次迭代中完成的,所以相比较于对集合进行多次遍历,性能会提升。
  • 灵活性 :
    序列操作的组合能够使得代码更加简洁,并且在处理复杂的数据处理逻辑时,提升可读性。

注意事项

  • 按需加载 : 由于惰性计算的性质,操作的结果直到消费时才会计算。因此,如果代码需要频繁访问中间结果,使用 asSequence()可能不太合适。
  • 不支持随机访问 : Sequence 不是随机访问的,不能使用索引来访问元素。当需要随机访问时,仍然需要使用集合类型。

7 lazy 使用

在 Kotlin 中,lazy 是一个用于延迟初始化的功能,它可以用于创建重量级对象的延迟加载。通过将对象的创建延迟到首次使用时,可以有效地节省资源并提高性能。

作用

  • 延迟初始化 : 使用lazy,对象的创建被推迟到实际需要时。这对于重量级对象(资源消耗高的对象,如数据库连接、网络请求、复杂计算结果等)尤其有用,因为它可以避免不必要的资源占用。
  • 线程安全 : lazy 的实现是线程安全的,当多个线程尝试同时访问该属性时,Kotlin会确保该对象只会被初始化一次。可以通过不同的线程安全策略创建 lazy 属性,例如LazyThreadSafetyMode.SYNCHRONIZED、PUBLICATION 或 NONE。
  • 提高性能 :通过延迟初始化,程序可以在不需要该对象时节省内存和资源,进而提高性能,尤其是在构造对象代价昂贵的情况下。

使用方法

在 Kotlin 中,定义一个 lazy 属性非常简单。你可以使用 lazy 函数来定义一个延迟初始化属性。

class HeavyObject {init {println("HeavyObject is initialized")}
}class MyClass {// 使用 lazy 创建重对象,只有在第一次访问时才会被实例化val heavyObject: HeavyObject by lazy {HeavyObject()}
}fun main() {val myClass = MyClass()println("MyClass instance is created")// 访问 heavyObject,触发初始化println("Accessing heavyObject...")myClass.heavyObject// 再次访问 heavyObject,不会重新初始化println("Accessing heavyObject again...")myClass.heavyObject
}

您可以指定 lazy 的线程安全模式,来满足不同的需求:

val threadSafeLazy: HeavyObject by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {HeavyObject()
}
http://www.dtcms.com/a/506927.html

相关文章:

  • 如何使用思维导图提升信息整理效率
  • K-VXE-TABLE二次封装,含table‘自定义列功能
  • 基于 GEE 开发的一种利用 OTSU 算法实现水体提取的便捷工具
  • Linux小课堂: 深入解析 top、htop、glances 及进程终止机制
  • 建设协会网站洛阳伟创科技
  • MongoDB 提供的 `GridFSTemplate` 操作 GridFS 大文件系统的常用查询方式
  • 2025年ASOC SCI2区TOP,基于模糊分组的多仓库多无人机电力杆巡检模因算法,深度解析+性能实测
  • 无人机地面站中不同的飞行模式具体含义释义(开源飞控常用的5种模式)
  • Inventor 转换为 3DXML 全流程技术指南:附迪威模型网在线方案
  • Maven POM 简介
  • pytorch踩坑记录
  • seo每天一贴博客南宁网站排名优化电话
  • 手机端网站开发书籍徐州vi设计公司
  • STM32F1和STM32F4在配置硬件SPI1时有什么不同?
  • 衣柜灯橱柜灯MCU方案开发
  • 数据访问对象模式(Data Access Object Pattern)
  • 滚动显示效果
  • Spring Cloud - Spring Cloud 微服务概述 (微服务的产生与特点、微服务的优缺点、微服务设计原则、微服务架构的核心组件)
  • YOLOv4:目标检测领域的 “速度与精度平衡大师”
  • agent设计模式:第二章节—路由
  • 玩转Docker | 使用Docker安装uptime-kuma监控工具
  • flutter开发小结
  • 【运维】鲲鹏麒麟V10 操作系统aarch64自制OpenSSH 9.8p1 rpm包 ssh漏洞修复
  • react学习(五) ---- hooks
  • 【C语言】程序的编译和链接(基础向)
  • 基于单片机的热量计测量系统设计
  • 显卡功能及原理介绍
  • 丽水网站建设明恩玉杰百度网址导航
  • 时序数据库选型指南:从大数据视角看IoTDB的核心优势
  • 免费域名网站的网站后台用什么做