Kotlin 作用域函数 let 的实现原理示例解析
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>示例代码</li><li>实现原理</li><li>编译后字节码</li><li>常见用途</li></ul></div><p>Kotlin 中的 <code>let</code> 是一个 <strong>标准库扩展函数</strong>,它广泛用于作用域函数(Scope Functions)中,尤其适用于对可空对象(nullable)做非空判断并执行代码块的场景。</p><p class="maodian"></p><h2>示例代码</h2>
<div class="jb51code"><pre class="brush:java;">val name: String? = "123"
name?.let {
println(it)
}</pre></div>
<p>这个例子等价于:</p>
<div class="jb51code"><pre class="brush:java;">if (name != null) {
val it = name
println(it)
}</pre></div>
<p>也就是说,<code>name?.let { ... }</code> 只有当 <code>name</code> 非空时才执行 <code>let</code> 的 lambda 块。lambda 表达式中 <code>it</code> 就是 <code>name</code> 的非空值。<code>let</code> 返回 lambda 的返回值。</p>
<p class="maodian"></p><h2>实现原理</h2>
<p>Kotlin 的 <code>let</code> 函数定义在 <code>commonMain/kotlin/util/Standard.kt</code> 中,源码如下:</p>
<div class="jb51code"><pre class="brush:java;">@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}</pre></div>
<p>它是一个 <strong>内联(inline)函数</strong>,在编译时会被内联展开,避免 lambda 带来的性能开销。泛型 <code><T, R></code> 表示接收一个类型为 <code>T</code> 的对象,返回一个类型为 <code>R</code> 的结果。<code>T.let</code> 表示 <code>let</code> 是类型 <code>T</code> 的扩展函数。<code>block: (T) -> R</code> 是接收 <code>T</code> 的函数(lambda 表达式)。也就是说,它只是将当前对象 <code>this</code> 传入了 <code>block(this)</code> 中。</p>
<p><code>@kotlin.internal.InlineOnly</code>,这是一个 <strong>注解(Annotation)</strong>,用于标记某个函数 <strong>只能在被 inline(内联)时使用</strong>,否则编译器会报错。</p>
<p>比如下面的代码:</p>
<div class="jb51code"><pre class="brush:java;">@InlineOnly
inline fun <T> T.let(block: (T) -> Unit): Unit {
block(this)
}</pre></div>
<p>表示这个 <code>let</code> 函数 <strong>不会生成实际函数调用</strong>(它只能内联展开),避免 Java 或非 Kotlin 编译器调用这个方法。</p>
<p>为什么要限制只能 inline?为了提高性能,避免生成函数对象和调用开销,同时 <strong>确保代码安全地被内联使用</strong>,防止其他模块通过反射或 Java 调用这个方法。</p>
<div class="jb51code"><pre class="brush:java;">contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}</pre></div>
<p>这是Kotlin 的 <strong>Contract DSL</strong>,用于 <strong>给编译器更多关于 lambda 执行行为的信息</strong>,提升智能分析、空安全和优化。表示<code>block</code> 这个 lambda 参数,在函数调用过程中会 <strong>被调用且只调用一次</strong>(Exactly once)。这使得编译器可以进行一些静态分析优化,例如在下面这种空检查中,判断 <code>name</code> 非空:</p>
<div class="jb51code"><pre class="brush:java;">val name: String? = "abc"
name?.let {
// 编译器知道这里 it 一定非空,不会再要求你加 !!
println(it.length) // 安全
}</pre></div>
<p><code>InvocationKind</code> 类型说明:</p>
<ul><li><code>EXACTLY_ONCE</code>:block 会被调用且仅一次</li><li><code>AT_LEAST_ONCE</code>:一定会调用一次或多次</li><li><code>AT_MOST_ONCE</code>:最多一次,可能不调用</li><li><code>UNKNOWN</code>:不确定</li></ul>
<p>编译器依靠这个信息进行<strong>控制流分析</strong>,提升非空智能推断、性能优化、检测死代码等能力。</p>
<p><code>R</code> 是泛型返回类型。</p>
<div class="jb51code"><pre class="brush:java;">inline fun <T, R> T.let(block: (T) -> R): R {
return block(this)
}</pre></div>
<p><code><T, R></code> 是泛型声明,<code>T</code>是调用 <code>let</code> 的对象类型(接收者),<code>R</code>是<code>block</code> 函数返回值的类型,也是 <code>let</code> 函数的最终返回值类型。</p>
<p>举例:</p>
<div class="jb51code"><pre class="brush:java;">val name = "abc"
val length: Int = name.let { it.length } // block 返回 Int,所以 R = Int</pre></div>
<p>也可以是任意类型:</p>
<div class="jb51code"><pre class="brush:java;">val upper = "abc".let { it.uppercase() } // R = String
val printResult = "abc".let { println(it) } // R = Unit</pre></div>
<p class="maodian"></p><h2>编译后字节码</h2>
<p>比如:</p>
<div class="jb51code"><pre class="brush:java;">val name: String? = "123"
name?.let {
println(it)
}</pre></div>
<p>大致翻译成 Java 是:</p>
<div class="jb51code"><pre class="brush:java;">String name = "123";
if (name != null) {
System.out.println(name);
}</pre></div>
<p>编译器把 <code>?.let { ... }</code> 直接转成了 <code>if != null</code> 的判断。lambda 是内联展开的,不会有额外函数对象生成,所以效率非常高。</p>
<p class="maodian"></p><h2>常见用途</h2>
<p><strong>处理 nullable 类型</strong>:</p>
<div class="jb51code"><pre class="brush:java;">val name: String? = getName()
name?.let {
println("非空值是:$it")
}</pre></div>
<p><strong>链式调用</strong>:</p>
<div class="jb51code"><pre class="brush:java;">val result = listOf(1, 2, 3).map { it * 2 }.let {
it.joinToString()
}</pre></div>
<p><strong>限定作用域变量</strong>(避免变量污染):</p>
<div class="jb51code"><pre class="brush:java;">val userInput = readLine()
userInput?.let {
val trimmed = it.trim()
println("你输入的是:$trimmed")
}
// trimmed 在此作用域外不可见</pre></div>
<p>到此这篇关于Kotlin 作用域函数 let 的实现原理的文章就介绍到这了,更多相关Kotlin 作用域函数 let内容请搜索琼殿技术社区以前的文章或继续浏览下面的相关文章希望大家以后多多支持琼殿技术社区!</p>
<div class="art_xg">
<b>您可能感兴趣的文章:</b><ul><li>Kotlin 作用域函数apply、let、run、with、also使用指南</li><li>Kotlin作用域函数使用示例详细介绍</li><li>Kotlin作用域函数应用详细介绍</li><li>Kotlin创建一个好用的协程作用域</li><li>Kotlin作用域函数之间的区别和使用场景详解</li><li>Kotlin中let、run、with、apply及also的用法和差别</li><li>Kotlin中标准函数run、with、let、also与apply的使用和区别详解</li></ul>
</div>
</div>
<!--endmain-->
頁:
[1]