Kotlin - 函数的上下文参数 Context Parameters

一、概念

Context Receivers 是在 Kotlin v1.6.20 中作为实验性引入的,在 v2.2.0 中更改为 Context Parameters,在 v2.4.0 转正。

主要用来解决带接收者的扩展函数只有一个上下文的局限性,提供一到多个上下文,组合起来实现更多的功能调用。简化依赖项注入,、改进 DSL 设计、 以及范围操作, 简化了依赖的管理

  • 构造器不能声明上下文参数。
  • 上下文参数的属性不能拥有后端域变量(Backing Field), 也不能拥有初始化器。
  • 带上下文参数的属性 不能使用委托。

二、使用 Context Parameters

2.1 启用特性

v2.4.0+ 就不需要了。

kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xcontext-parameters")
    }
}

2.2 单个上下文参数的使用

2.2.1 定义函数

使用 context 关键字声明上下文参数。

//上下文可以是接口也可以是类
interface UserService {
    fun findUserById(id: Long): User
}

//定义带有上下文的函数
context(userService: UserService)
fun userTools(id: Long) {
    userService.findUserById(id)    //使用上下文的功能
}

//定义带有上下文的扩展函数
context(userService: UserService)
fun Long.findUser() = userService.findUserById(this)

2.2.2 调用

虽然调用处能直接使用上下文参数的功能,但重点是在于对 UserService 的使用封装在了 userTools() 中,这里是要使用 userTools() 这个封装好的功能。

//调用的地方要提供上下文环境(外部传入)
fun demo1(userService: UserService) {
    with(userService) {
        val user1 = userTools(123)
        val user2 = 123L.findUser()
    }
}

//调用的地方要提供上下文环境(自己创建)
fun demo2() {
    val userService = object : UserService {
        override fun findUserById(id: Long): User = User(123)
    }
    with(userService) {
        val user1 = userTools(123)
        val user2 = 123L.findUser()
    }
}

2.3 多个上下文参数的使用

interface UserService {
    fun findUserById(id: Long): User
}
interface LogService {
    fun printId(id: Long) = println(id)
}

context(userService: UserService)
fun userTools(id: Long) {
    userService.findUserById(id)
}
context(logService: LogService)
fun logTools(id: Long) {
    logService.printId(id)
}

2.3.1 定义函数

//带多个上下文的函数
fun twoTools(userService: UserService, logService: LogService) {
    //调用带上下文参数的函数(可控制作用域)
    with(userService) {
        userTools(123)
        with(logService) {
            userTools(123)
            logTools(123)
            twoTools(123)
        }
    }
}

//带多个上下文的扩展函数
context(userService: UserService, logService: LogService)
fun Long.twoTools() {
    val user = userTools(this)
    logTools(this)
}

2.3.2 调用

fun demo(userService: UserService, logService: LogService) {
    //可控制作用域
    with(userService) {
        userTools(123)
        with(logService) {
            userTools(123)
            logTools(123)
            val user1 = twoTools(123)
            val user2 = 123L.twoTools()
        }
    }
}

2.4 函数重载(显式上下文参数)

可以在调用时指定到底用哪个。这个特性还可以减少 whith() 函数带来的嵌套,如果同一组上下文要在多个调用里复用,继续用 whith() 写会更合适。

class EmailSender
class SmsSender

context(emailSender: EmailSender)
fun sendNotification() {
    println("Sent email notification")
}

context(smsSender: SmsSender)
fun sendNotification() {
    println("Sent SMS notification")
}

context(defaultEmailSender: EmailSender, defaultSmsSender: SmsSender)
fun notifyUser() {
    // 选择带 EmailSender 上下文参数的重载
    sendNotification(emailSender = defaultEmailSender)
    // 选择带 SmsSender 上下文参数的重载
    sendNotification(smsSender = defaultSmsSender)
}

三、已过时的 Context Receiver

3.1 启用特性

kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xcontext-receivers")
    }
}

3.2 使用

interface LoggingContext {
    val logger: Logger
}

// 使用 context 关键字声明上下文接收者
context(LoggingContext)
fun processData(data: String) {
    logger.info("Processing $data")  // 可以直接访问 logger
}

// 调用时不需要显式传递,但需要在作用域内有合适的上下文
with(object : LoggingContext {
    override val logger = Logger.getLogger()
}) {
    processData("some data")  // 自动使用当前的 LoggingContext
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值