补充一些杂七杂八有用的东西
Kotlin 中的 run
、let
、with
、apply
都是作用域函数 (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)
如何决定用哪个?
- 处理非空对象 →
let
(?.let
是经典用法) - 对象配置初始化 →
apply
(返回自身,适合链式设置) - 对同一对象执行多操作并返回结果 →
with
(非空对象)或run
(可空对象用?.run
) - 组合初始化与计算 →
run
(尤其是需要返回计算结果时) - 创建局部作用域 →
let
或独立run
by lazy
:
在 Kotlin 中,by lazy
是一种延迟初始化 机制,用于在首次访问变量时才初始化其值,而不是在变量声明时就初始化。这种方式可以优化性能(避免提前创建资源),尤其适合初始化成本较高的对象(如大型数据结构、网络连接等)。
说白了就是类似懒加载 但是要注意
by lazy
只能用于不可变变量(val
) ,因为初始化后值不会再改变。- 初始化逻辑(lambda 表达式)仅在变量首次被访问时执行一次,后续访问直接返回已初始化的值。
它只能是val 类型的
lateinit
关键字
by lazy
和 lateinit
都用于延迟初始化,但适用场景不同:
特性 | 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
