kotlin 中的 let
是一个 标准库扩展函数,它广泛用于作用域函数(scope functions)中,尤其适用于对可空对象(nullable)做非空判断并执行代码块的场景。
示例代码
val name: string? = "123" name?.let { println(it) }
这个例子等价于:
if (name != null) { val it = name println(it) }
也就是说,name?.let { ... }
只有当 name
非空时才执行 let
的 lambda 块。lambda 表达式中 it
就是 name
的非空值。let
返回 lambda 的返回值。
实现原理
kotlin 的 let
函数定义在 commonmain/kotlin/util/standard.kt
中,源码如下:
@kotlin.internal.inlineonly public inline fun <t, r> t.let(block: (t) -> r): r { contract { callsinplace(block, invocationkind.exactly_once) } return block(this) }
它是一个 内联(inline)函数,在编译时会被内联展开,避免 lambda 带来的性能开销。泛型 <t, r>
表示接收一个类型为 t
的对象,返回一个类型为 r
的结果。t.let
表示 let
是类型 t
的扩展函数。block: (t) -> r
是接收 t
的函数(lambda 表达式)。也就是说,它只是将当前对象 this
传入了 block(this)
中。
@kotlin.internal.inlineonly
,这是一个 注解(annotation),用于标记某个函数 只能在被 inline(内联)时使用,否则编译器会报错。
比如下面的代码:
@inlineonly inline fun <t> t.let(block: (t) -> unit): unit { block(this) }
表示这个 let
函数 不会生成实际函数调用(它只能内联展开),避免 java 或非 kotlin 编译器调用这个方法。
为什么要限制只能 inline?为了提高性能,避免生成函数对象和调用开销,同时 确保代码安全地被内联使用,防止其他模块通过反射或 java 调用这个方法。
contract { callsinplace(block, invocationkind.exactly_once) }
这是kotlin 的 contract dsl,用于 给编译器更多关于 lambda 执行行为的信息,提升智能分析、空安全和优化。表示block
这个 lambda 参数,在函数调用过程中会 被调用且只调用一次(exactly once)。这使得编译器可以进行一些静态分析优化,例如在下面这种空检查中,判断 name
非空:
val name: string? = "abc" name?.let { // 编译器知道这里 it 一定非空,不会再要求你加 !! println(it.length) // 安全 }
invocationkind
类型说明:
exactly_once
:block 会被调用且仅一次at_least_once
:一定会调用一次或多次at_most_once
:最多一次,可能不调用unknown
:不确定
编译器依靠这个信息进行控制流分析,提升非空智能推断、性能优化、检测死代码等能力。
r
是泛型返回类型。
inline fun <t, r> t.let(block: (t) -> r): r { return block(this) }
<t, r>
是泛型声明,t
是调用 let
的对象类型(接收者),r
是block
函数返回值的类型,也是 let
函数的最终返回值类型。
举例:
val name = "abc" val length: int = name.let { it.length } // block 返回 int,所以 r = int
也可以是任意类型:
val upper = "abc".let { it.uppercase() } // r = string val printresult = "abc".let { println(it) } // r = unit
编译后字节码
比如:
val name: string? = "123" name?.let { println(it) }
大致翻译成 java 是:
string name = "123"; if (name != null) { system.out.println(name); }
编译器把 ?.let { ... }
直接转成了 if != null
的判断。lambda 是内联展开的,不会有额外函数对象生成,所以效率非常高。
常见用途
处理 nullable 类型:
val name: string? = getname() name?.let { println("非空值是:$it") }
链式调用:
val result = listof(1, 2, 3).map { it * 2 }.let { it.jointostring() }
限定作用域变量(避免变量污染):
val userinput = readline() userinput?.let { val trimmed = it.trim() println("你输入的是:$trimmed") } // trimmed 在此作用域外不可见
到此这篇关于kotlin 作用域函数 let 的实现原理的文章就介绍到这了,更多相关kotlin 作用域函数 let内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论