编程中级Kotlin开发者

在Kotlin中,scope函数(let,also,run,apply,with)是什么?这些函数有什么区别,如何针对不同的任务选择它们,使用时可能会遇到哪些细微差别?请给出示例。

用 Hintsage AI 助手通过面试

答案。

Kotlin中的Scope函数("域函数")是标准函数(letalsorunapplywith),它们允许管理对象代码块的执行上下文。它们之间的区别在于:

  • 返回值的类型,
  • 在块内部访问对象的方式:通过it或通过this

简要比较:

函数this/it返回值用途
letit结果操作链,处理nullable,映射
alsoit对象副作用,日志,调试
runthis结果计算,带返回值的初始化
applythis对象对象配置,构建器
withthis结果与外部API工作,对象 "外部"

示例:

  • let:如果对象为nullable,则很方便:
val str: String? = "Text" str?.let { println(it.length) }
  • apply:对象设置:
val paint = Paint().apply { color = Color.RED strokeWidth = 2f }
  • run:在对象上执行,返回结果:
val length = "abcde".run { length }
  • with:用于与外部对象工作:
val sb = StringBuilder() with(sb) { append("Hello, ") append("world!") toString() }
  • also:用于副作用(例如,日志):
val list = mutableListOf(1, 2, 3) list.also { println("Before: $it") }.add(4)

需要注意的事项:

  • letit中创建对象的副本,修改对象的属性不太方便。
  • applyalso始终返回对象本身(this / it),对构建器很有用。
  • run和with常被混淆:with是普通函数,不是扩展。

有陷阱的问题。

let和also之间有什么区别?

答案:

  • 两者在块内都使用it
  • let返回lambda的结果,通常用于转换链,
  • also返回原始对象,用于副作用(日志,调试),以避免干扰转换链。

示例:

val result = listOf(1).also { println(it) }.map { it * 2 } // 结果 — List<Int>

由于对主题细微差别的不了解而导致的实际错误示例:


故事

新手使用let来配置对象,以为这样可以“在链中”更改其状态。结果在配置块结束时得到的不是对象,而是lambda的结果(例如,什么都没有),导致DSL构建链被破坏。


故事

在编写处理nullable对象的代码时,使用了run而不是let,没有注意到返回值的不同。最终表达式的结果与预期不同,使得null出现在不该出现的地方——应用逻辑出现了问题。


故事

在一个大型构建器中,意外地对内部对象使用了with,期待扩展模式。由于with不是扩展函数,由多个with块构成的链条工作不正确,内部调用混淆并超出了当前对象的范围。不得不完全重写对象创建的层次结构。