Kotlin 作用域函数 let 的实现原理

Kotlin 中的 let 是一个 标准库扩展函数,它广泛用于作用域函数(Scope Functions)中,尤其适用于对可空对象(nullable)做非空判断并执行代码块的场景。

示例代码

val name: String? = "123"
name?.let {println(it)
}

这个例子等价于:

if (name != null) {val it = nameprintln(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 的对象类型(接收者),Rblock 函数返回值的类型,也是 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 在此作用域外不可见

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/90138.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/90138.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从FDTD仿真到光学神经网络:机器学习在光子器件设计中的前沿应用工坊

FDTD仿真与光学神经网络的基础概念 FDTD&#xff08;时域有限差分&#xff09;是一种数值方法&#xff0c;用于求解麦克斯韦方程组&#xff0c;广泛应用于光子器件设计。光学神经网络通过光波导、衍射元件等物理结构实现矩阵运算&#xff0c;具有低能耗、高并行的优势。 机器学…

在Ubutu22系统上面离线安装Go语言环境【教程】

0.引言 Go语言&#xff08;又称Golang&#xff09;是Google开发的一种静态强类型、编译型、并发型编程语言&#xff0c;由Robert Griesemer、Rob Pike和Ken Thompson于2007年开始设计&#xff0c;2009年正式发布。 1.到官网下载压缩包 2.从win10系统离线上传压缩包给ubuntu22…

CMake实践:CMake3.30版本之前和之后链接boost的方式差异

目录 1.背景 2.boost引入CMake时机 3.CMake 3.30 之前&#xff08;含 3.29&#xff09;链接 Boost 的方式 4.CMake 3.30 及之后链接 Boost 的方式 5.CMake3.30后引入Boost的步骤 6.迁移建议&#xff08;3.30 之前 → 3.30 之后&#xff09; 7.CMake 3.30 移除FindBoost的…

告别挂马风险!PBootCMS完美替代方案BadouCMS

开发企业网站时一直比较喜欢用pbootcms,标签套用很简单&#xff0c;使用也方便。 但是pbootcms一直有被挂马的问题&#xff0c;官方好像也不怎么更新了&#xff01;换过好几个cms&#xff0c;比如eyoucms、dedecms、帝国等等&#xff0c;感觉都不怎么能用得习惯&#xff0c;还…

开发者如何集成AI绘画?智创聚合API简化Midjourney接入

在 AI 绘画领域&#xff0c;Midjourney 的大名如雷贯耳&#xff0c;其强大的图像生成能力&#xff0c;能将我们脑海中的奇思妙想&#xff0c;迅速转化为精美的视觉画面&#xff0c;深受设计师、艺术家以及广大创意爱好者的青睐。然而&#xff0c;使用 Midjourney 的过程中&…

pycharm回车、删除、方向键和快捷键等不能使用原因

解决方法 &#xff1a;菜单栏中的Tools取消勾选Vim Emulator 原因 &#xff1a;新版的pycharm安装中&#xff0c;默认安装了vim扩展&#xff0c;一旦安装了pycharm在编写代码时会默认使用Vim编辑器

修复ffmpeg.dll丢失错误|6种解决ffmpeg.dll方法详细教程

看到电脑提示“ffmpeg.dll丢失”&#xff0c;很多人会懵。ffmpeg.dll 是个处理视频、音频的关键文件。它要是没了或坏了&#xff0c;软件就打不开或直接闪退。常见原因是软件安装不全、文件被删、或者中病毒。下面说说它是干嘛的&#xff0c;再给解决办法。一.ffmpeg.dll 到底是…

OkHttp 与 Stetho 结合使用:打造强大的 Android 网络调试工具链

前言在 Android 应用开发过程中&#xff0c;网络请求的调试一直是一个重要但具有挑战性的环节。Facebook 开发的 Stetho 是一个强大的调试工具&#xff0c;当它与 OkHttp 结合使用时&#xff0c;可以为我们提供前所未有的网络请求洞察能力。本文将详细介绍如何将这两者结合使用…

LangGraph教程10:LangGraph ReAct应用

文章目录 ReAct 预构建的代理 向 ReAct 代理添加记忆 向 ReAct 代理添加系统提示 向 ReAct 代理添加人机交互 ReAct 官方文档地址:https://langchain-ai.github.io/langgraph/how-tos/#prebuilt-react-agent 中文文档地址:https://www.aidoczh.com/langgraph/how-tos/#react…

安卓第一个项目

测试所有摄像头 安卓CameraX&#xff1a;https://developer.android.com/media/grow/spatial-audio?hlzh-cn 1、MainActivity.java // 定义包名 package com.mms.densenapplication;// 引入 AppCompatActivity&#xff0c;支持兼容性更强的 Activity import androidx.appcompa…

Google Gemini 体验

文章中代码仓库 gemini 谷歌推出的 AI 只能模型 Gemini官网Gemini ChatGemini开发者文档Gemini SDK 所有模型 模型变体输入输出优化目标Gemini 2.5 Pro gemini-2.5-pro音频、图片、视频、文本和 PDF文本增强的思考和推理能力、多模态理解能力、高级编码能力等Gemini 2.5 Fla…

Trae安装指定版本的插件

前情 Trae是属于国产的跟 Cursor类似的AI编程IDE&#xff0c;我也是第一时间体验Trae的&#xff0c;虽然相比Cursor弱了一些&#xff0c;但是也绝对胜任了&#xff0c;前端因为排队问题我转战了Cursor&#xff0c;等到Trae出收费模式前&#xff0c;我已经办了Cursor会员了&…

【技术追踪】用于医学图像合成和分割的噪声一致孪生扩散模型(CVPR-2025)

孪生扩散模型&#xff0c;生成息肉图像用于提升分割性能&#xff01; 论文&#xff1a;Noise-Consistent Siamese-Diffusion for Medical Image Synthesis and Segmentation 代码&#xff1a;https://github.com/Qiukunpeng/Siamese-Diffusion 0、摘要 深度学习已彻底革新医学影…

Crontab详解

crontab是Unix/Linux系统中用于设置周期性任务的工具&#xff0c;通过编辑配置文件实现定时执行命令或脚本。以下是其语法规则和核心要点&#xff1a; 一、基本格式 * * * * * command - - - - - | | | | | | | | | ----…

中国1km逐月潜在蒸散发数据集 - matlab按shp批量裁剪

中国1km逐月潜在蒸散发数据集 - matlab按shp批量裁剪 1. 数据概述 2 利用掩膜文件对数据进行裁剪 3 完整代码 4 结语 本篇继续处理气象数据,中国1km逐月潜在蒸散发数据集同前节介绍的中国1km降水数据集一样,都可以从国家青藏高原科学数据中心获得,数据具有同样的空间分辨率(…

Node.js链接MySql

前言&#xff1a; 在现代 Web 开发和后端服务中&#xff0c;Node.js 因其高性能和异步特性被广泛使用。MySQL 作为流行的关系型数据库之一&#xff0c;提供了稳定高效的数据存储和管理能力。将 Node.js 与 MySQL 结合&#xff0c;可以构建强大的数据驱动型应用。 一、环境准备…

Charles 的 Windows proxy 对爬取瑞数6 网站接口数据的作用分析

其实本文还是源于上个月的这篇文章 ➡️▼ 耗时两天半&#xff0c;利用 DrissionPage绕过瑞数6&#xff0c;爬取某药*局数据经历~ 不同点是&#xff0c;当时爬取的是列表页&#xff08;已爬完&#xff09;&#xff0c;后面爬取的是详情页&#xff01;懂的都懂&#xff0c;差别还…

PHP 测验

PHP 测验 引言 PHP 作为一种流行的开源服务器端脚本语言,被广泛应用于网页开发、服务器端编程等领域。为了帮助大家更好地理解和掌握 PHP,我们特此推出本 PHP 测验。通过以下问题,您可以检验自己的 PHP 知识水平,同时也能了解自己在哪些方面需要加强。 测验内容 问题一…

阶段1--Linux中的文件服务器(FTP、NAS、SSH)

目录 一、FTP Server 1.1.简介 1.2.FTP基础 1.2.1.控制端口 1.2.2.数据端口 1.3.FTP Server默认配置 1.3.1.安装vsftp 1.3.2.准备分发的文件 1.3.3.启动服务 1.3.4.关闭防火墙 1.4.FTP Client&#xff08;默认仅能下载文件&#xff09; 1.4.1.LinuxFTP客户端程序1&#xff1a;l…

SpringBoot与Vue实战:高效开发秘籍

Spring Boot 是什么? Spring Boot 简介 Spring Boot 是基于 Spring 框架的快速开发工具,旨在简化 Spring 应用的初始搭建和开发过程。它通过约定大于配置的原则,提供自动配置、内嵌服务器和依赖管理等功能,使开发者能够快速构建独立运行的、生产级别的应用。 核心特点 …