Kotlin基本用法《四》-又想到了一些

补充一些杂七杂八有用的东西

Kotlin 中的 runletwithapply 都是作用域函数 (Scope Functions),它们的核心作用是创建一个临时作用域,简化代码并增强可读性 。虽然功能相似,但适用场景不同,主要区别在于接收者(this/it)的使用方式返回值

函数 接收者引用 返回值 适用场景
let it(默认) lambda 最后一行结果 处理非空对象、转换对象、限定局部变量作用域
run this lambda 最后一行结果 组合对象初始化与结果计算、替代临时变量
with this lambda 最后一行结果 对同一对象执行多操作(无空安全检查)
apply this 调用者本身(接收者) 对象配置初始化(如设置属性)、链式调用

let

特点

  • it 引用接收者(可自定义名称)
  • 返回 lambda 最后一行的结果
  • 常用 ?.let 形式处理非空对象(空安全
rust 复制代码
val str: String? = "Hello"

// 仅当 str 非空时执行(避免手动写 if (str != null))
str?.let {
    println("长度:${it.length}")  // it 指代 str 的非空值
    it.uppercase()  // 最后一行作为返回值
} ?: "默认值"  // 可配合 ?: 处理空情况

// 输出:长度:5,返回 "HELLO"
"默认值"  // 可配合 ?: 处理空情况

let 用法,默认 it 就指代这个str,当然你也可以不用it,改成ele, 这个随你

rust 复制代码
str?.let { ele ->
   println("长度:${ele.length}")  // it 指代 str 的非空值
    ele.uppercase()  // 最后一行作为返回值 
}
java 复制代码
// 临时变量仅在 let 作用域内可见
val result = "test".let { temp ->
    val upper = temp.uppercase()
    upper + "123"
}
println(result)  // 输出:TEST123

run

  • 特点

  • this 引用接收者(可省略)

  • 返回 lambda 最后一行的结果

  • 有两种用法:对象.run { ... } 或独立 run { ... }

kotlin 复制代码
data class User(var name: String, var age: Int)

val user = User("Alice", 20)

// 对 user 执行多步操作,并返回计算结果
val isAdult = user.run {
    name = "Alicia"  // 省略 this,直接访问属性
    age += 5
    age >= 18  // 返回布尔值
}

println(user)  // 输出:User(name=Alicia, age=25)
println(isAdult)  // 输出:true

run 用法,默认 就指代自身,即user, 形成一个闭包区域,节省代码,你想想,如果没有闭包 如果user属性很多,那么我就会这么写

ini 复制代码
user.name ="Alicia" 
user.age =25
user.name ="Alicia" 
uesr.height = 170
user.weight = 80

如果有闭包,那么就是

ini 复制代码
user?.run {
    name ="Alicia" 
    age =25
    height =180
    weight = 80
}

额外用法

ini 复制代码
// 独立 run 创建局部作用域,避免变量污染
val total = run {
    val a = 10
    val b = 20
    a + b
}
println(total)  // 输出:30

with:对同一对象执行多操作

  • 特点

    • 第一个参数是接收者,用 this 引用(可省略)
    • 返回 lambda 最后一行的结果
    • 无空安全检查(接收者为 null 时会崩溃)

因为它的特点,这个我用的比较少,没有空安全检查。

scss 复制代码
val list = mutableListOf<String>()

// 对 list 执行一系列操作
val size = with(list) {
    add("Apple")
    add("Banana")
    add("Cherry")
    size  // 返回集合大小
}

println(list)  // 输出:[Apple, Banana, Cherry]
println(size)  // 输出:3

如果list是这么声明的话 即 var list: mutableListOf? = null 那么with(list)就会崩 若接收者可能为 null,需先判断非空:

javascript 复制代码
val str: String? = "test"
if (str != null) {
    with(str) {
        println(length)  // 安全调用,需先确保 str 非空
    }
}

apply:对象配置与初始化

特点

  • this 引用接收者(可省略)
  • 返回接收者本身(便于链式调用)
  • 常用于对象创建后设置属性(替代 builder 模式)

经典用法:

scss 复制代码
// 创建并配置 TextView(Android 场景)
val textView = TextView(context).apply {
    text = "Hello"
    textSize = 16f
    setTextColor(Color.BLACK)
    setPadding(10, 10, 10, 10)
}
// apply 返回 textView 本身,可直接使用
layout.addView(textView)

如何决定用哪个?

  1. 处理非空对象let?.let 是经典用法)
  2. 对象配置初始化apply(返回自身,适合链式设置)
  3. 对同一对象执行多操作并返回结果with(非空对象)或 run(可空对象用 ?.run
  4. 组合初始化与计算run(尤其是需要返回计算结果时)
  5. 创建局部作用域let 或独立 run

by lazy

在 Kotlin 中,by lazy 是一种延迟初始化 机制,用于在首次访问变量时才初始化其值,而不是在变量声明时就初始化。这种方式可以优化性能(避免提前创建资源),尤其适合初始化成本较高的对象(如大型数据结构、网络连接等)。

说白了就是类似懒加载 但是要注意

  • by lazy 只能用于不可变变量(val ,因为初始化后值不会再改变。
  • 初始化逻辑(lambda 表达式)仅在变量首次被访问时执行一次,后续访问直接返回已初始化的值。

它只能是val 类型的

lateinit关键字

by lazylateinit 都用于延迟初始化,但适用场景不同:

特性 by lazy lateinit var
变量类型 只能用于 val(不可变) 只能用于 var(可变)
初始化时机 首次访问时自动执行初始化逻辑 需手动调用初始化代码(无自动逻辑)
适用场景 初始化逻辑明确(可在 lambda 中写) 初始化逻辑复杂(需外部调用)
类型限制 无(基本类型、对象都可) 只能用于非空对象类型(不能是基本类型)
kotlin 复制代码
// by lazy:自动执行初始化逻辑
val lazyValue by lazy { "自动初始化的值" }

// lateinit:需手动初始化
lateinit var lateValue: String
fun initLateValue() {
    lateValue = "手动初始化的值"  // 必须在使用前调用
}

fun main() {
    println(lazyValue)  // 自动初始化
    initLateValue()     // 手动初始化
    println(lateValue)
}

lateinit 声明的对象,编译器不会检测它是否为空,默认coder会让它不为空,那么这就埋下了隐患,如果最后运行的时候,这个属性为空,那么还是会崩溃

object

object 关键字,可以直接声明单例类,再也不用像java 一样,什么懒汉,饿汉式了,双重校验了。其实这个object,就是类似java中静态方法式声明单例。

kotlin 复制代码
object DisplayUtils { 
    fun px2dip(context: Context, pxValue: Float): Int {
        val scale = context.resources.displayMetrics.density
        return (pxValue / scale + RATE).toInt()
    }
}

但是在kotlin 中,object还有一个作用,

kotlin 复制代码
OSSUpload.with(context)
    .projectName("user_pic")
    .city(city)
    .clearTime(OSSClearTime.oneMonth)
    .uris(uploadUris)
    .upload(object : OSSUploadListener {
        override fun progress(i: Int, l: Long, l1: Long) {
            listener?.onProgress()
        }

        override fun success(ossUploadResult: OSSUploadResult) {
            val urlList = ossUploadResult.urls.stream()
                .map { ossUploadUrl: OSSUploadUrl -> ossUploadUrl.url }
                .collect(Collectors.toList())

          
            listener?.onSuccess(urlList as ArrayList<String>)
        }

        override fun error(s: String, s1: String) {
            listener?.onFail(s)
            
        }
    })

能看出来么,就是声明一个匿名内部类 object : OSSUploadListener {}

说了这么多,听懂了啵,可以尝试用起来,为什么不用,tell me why baby

相关推荐
Kapaseker14 分钟前
详解 Compose background 的重组陷阱
android·kotlin
黄林晴39 分钟前
Kotlin 2.3.20-RC2 来了!JPA 开发者狂喜,6 大更新一文速览
android·kotlin
糖猫猫cc18 小时前
Kite:填充处理器
kotlin·orm·kite
Kapaseker1 天前
一杯美式深入理解 data class
android·kotlin
alexhilton3 天前
端侧RAG实战指南
android·kotlin·android jetpack
Kapaseker4 天前
2026年,我们还该不该学编程?
android·kotlin
Kapaseker5 天前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
Kapaseker6 天前
一杯美式搞定 Kotlin 空安全
android·kotlin
FunnySaltyFish7 天前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?
算法·kotlin·android jetpack
Kapaseker7 天前
Compose 进阶—巧用 GraphicsLayer
android·kotlin