Kotlin中扩展函数作为函数参数的优势及适用场景
Kotlin作用域函数:apply与also的差异及扩展函数高阶用法解析
先看apply和also的核心定义:
public inline fun <T> T.apply(block: T.() -> Unit): T { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } block() return this } public inline fun <T> T.also(block: (T) -> Unit): T { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } block(this) return this }
apply与also的核心差异
1. 参数类型本质不同
apply的参数是扩展函数类型T.() -> Unit:lambda内部默认以调用者对象作为this,可以直接调用对象的成员属性和方法,不需要额外引用。also的参数是普通函数类型(T) -> Unit:lambda需要通过参数(默认用it指代)来访问调用者对象,无法直接用this操作对象成员。
2. 使用场景各有侧重
从实际调用代码就能看出差异:
fun main() { val logger = Logger.getLogger("Test") logger.info("A").apply { // 这里this就是logger.info("A")的返回值,若返回值是Logger类型,可直接写info("B") logger.info("B") }.also { // 这里it是logger.info("A")的返回值,需通过it访问其成员 logger.info("C") } }
- apply适合对象初始化/内部修改:当你需要批量设置对象属性、调用成员方法时,用
apply可以省去重复写对象名的麻烦:val user = User().apply { name = "Alice" age = 25 setEmail("alice@example.com") } - also适合对象的外部操作:当你需要基于对象做一些附加操作(比如日志打印、存储到数据库、传入其他函数),但不需要修改对象内部状态时,用
also更清晰:val user = User("Alice", 25).also { println("Created user: ${it.name}") saveUser(it) }
扩展函数作为高阶函数参数的模式
这种模式的核心是为lambda赋予调用者对象的上下文,主要分两种形式:
- 接收者类型参数(
T.() -> R):lambda以调用者为接收者,内部可直接用this操作对象成员,典型代表是apply、run。 - 普通参数类型(
(T) -> R):lambda通过参数接收调用者对象,内部用参数(如it)访问对象,典型代表是also、let。
最佳实践
- 按需选择参数类型:如果lambda主要操作对象内部状态,用
T.() -> Unit;如果是用对象做外部操作,用(T) -> Unit。 - 保持lambda简洁:作用域函数的lambda应该只做单一职责的事,避免写过长的逻辑,否则会让代码可读性下降。
- 明确指代对象:嵌套使用作用域函数时,容易混淆
this和it,可以给参数命名来明确指代:user.apply { this.age = 26 // 明确指向user对象 }.also { currentUser -> println("Updated user: ${currentUser.name}") } - 避免过度滥用:作用域函数是为了简化代码,但连续嵌套多个反而会让逻辑零散,不如直接写普通代码清晰。
核心优势
- 代码更简洁:省去重复写对象名的冗余,直接通过
this或it访问对象,代码更紧凑。 - 逻辑更连贯:lambda内部的操作都围绕调用者对象展开,上下文清晰,可读性更强。
- 支持链式调用:作用域函数都返回原对象(或计算结果),可以链式调用多个操作,代码流程更流畅。
- 无性能损耗:这类函数都是
inline修饰的,编译时会把lambda代码直接嵌入调用处,不会产生额外的函数调用开销。
内容的提问来源于stack exchange,提问作者Andreas
相关产品推荐
相关产品推荐

