一、概念
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
}
1177

被折叠的 条评论
为什么被折叠?



