听闻少年二字,当与平庸相斥。这篇文章主要讲述揭秘 Kotlin 1.6.20 重磅功能 Context Receivers相关的知识,希望能为你提供帮助。
这篇文章我们一起来聊一下Kotlin 1.6.20
的新功能 Context Receivers
,来看看它为我们解决了什么问题。
通过这篇文章将会学习到以下内容:
- 扩展函数的局限性
- 什么是
Context Receivers
,以及如何使用 Context Receivers
解决了什么问题- 引入
Context Receivers
会带来新的问题,我们如何解决 Context Receivers
应用范围及注意事项
在 Kotlin 中接受者只能应用在扩展函数或者带接受者
lambda
表达式中, 如下所示。class Context
var density = 0f// 扩展函数
inline fun Context.px2dp(value: Int): Float = value.toFloat() / density
接受者是
fun
关键字之后和点之前的类型 Context
,这里隐藏了两个知识点。- 我们可以像调用内部函数一样,调用扩展函数
px2dp()
,通常结合 Kotlin 作用域函数with
,run
,apply
等等一起使用。
with(Context())
px2dp(100)
- 在扩展函数内部,我们可以使用
this
关键字,或者隐藏关键字隐式访问内部的成员函数,但是我们不能访问私有成员
- 只能定义一个接受者,因此限制了它的可组合性,如果有多个接受者只能当做参数传递。比如我们调用
px2dp()
方法的同时,往logcat
和file
中写入日志。
class LogContext
fun logcat(message: Any)class FileContext
fun writeFile(message: Any) fun printf(logContext: LogContext, fileContext: FileContext)
with(Context())
val dp = px2dp(100)
logContext.logcat("print $dp in logcat")
fileContext.writeFile("write $dp in file")
- 在 Kotlin 中接受者只能应用在扩展函数或者带接受者
lambda
表达式中,却不能在普通函数中使用,失去了灵活性
Context Receivers
的出现带来新的可能性,它通过了组合的方式,将多个上下文接受者合并在一起,灵活性更高,应用范围更广。什么是 Context Receivers
Context Receivers
用于表示一个基本约束,即在某些情况下需要在某些范围内才能完成的事情,它更加的灵活,可以通过组合的方式,组织上下文,将系统或者第三方类组合在一起,实现更多的功能。如果想在项目中使用
Context Receivers
,需要将 Kotlin 插件升级到 1.6.20
,并且在项目中开启才可以使用。plugins
id org.jetbrains.kotlin.jvm version 1.6.20// ......
kotlinOptions
freeCompilerArgs = ["-Xcontext-receivers"]
如何使用 Context Receivers
当我们完成上述配置之后,就可以在项目中使用
Context Receivers
,现在我们将上面的案例改造一下。context(LogContext, FileContext)
fun printf()
with(Context())
val dp = px2dp(100)
logContext.logcat("print $dp in logcat")
fileContext.writeFile("write $dp in file")
我们在
printf()
函数上,使用context()
关键字,在 context()
关键字括号中,声明上下文接收者类型的列表,多个类型用逗号分隔。但是列出的类型不允许重复,它们之间不允许有子类型关系。通过
context()
关键字来限制它的作用范围,在这个函数中,我们可以调用上下文 LogContext
、 FileContext
内部的方法,但是使用的时候,只能通过 Kotlin 作用域函数嵌套来传递多个接受者,也许在未来可能会提供更加优雅的方式。with(LogContext())
with(FileContext())
printf("I am DHL")
引入 Context Receivers 导致可读性问题
如果我们在
LogContext
和 FileContext
中声明了多个相同名字的变量或者函数,我们只能通过 this@Lable
语句来解决这个问题。context(LogContext, FileContext)
fun printf(message: String)
logcat("print message in logcat $this@LogContext.name")
writeFile("write message in file $this@FileContext.name")
正如你所见,在
LogContext
和 FileContext
中都有一个名为 name
的变量,我们只能通过 this@Lable
语句来访问,但是这样会引入一个新的问题,如果有大量的同名的变量或者函数,会导致 this
关键字分散到处都是,造成可读性很差。所以我们可以通过接口隔离的方式,来解决这个问题。interface LogContextInterface
val logContext:LogContextinterface FileContextInterface
val fileContext:FileContextcontext(LogContextInterface, FileContextInterface)
fun printf(message: String)
logContext.logcat("print message in logcat $logContext.name")
fileContext.writeFile("write message in file $fileContext.name")
通过接口隔离的方式,我们就可以解决
this
关键字导致的可读性差的问题,使用的时候需要实例化接口。val logContext = object : LogContextInterface
override val logContext: LogContext = LogContext()val fileContext = object : FileContextInterface
override val fileContext: FileContext = FileContext()with(logContext)
with(fileContext)
printf("I am DHL")
Context Receivers 应用范围及注意事项
当我们重写带有上下文接受者的函数时,必须声明为相同类型的上下文接受者。
interface Canvas
interface Shape
context(Canvas)
fun draw()class Circle : Shape
context(Canvas)
override fun draw()
我们重写了
draw()
函数,声明的上下文接受者必须是相同的,Context Receivers
不仅可以作用在扩展函数、普通函数上,而且还可以作用在类上。context(LogContextInterface, FileContextInterface)
class LogHelp
fun printf(message: String)
logContext.logcat("print message in logcat $logContext.name")
fileContext.writeFile("write message in file $fileContext.name")
在类
LogHelp
上使用了 context()
关键字,我们就可以在 LogHelp
范围内任意的地方使用 LogContext
或者 FileContex
。val logHelp = with(logContext)
with(fileContext)
LogHelp()logHelp.printf("I am DHL")
Context Receivers
除了作用在扩展函数、普通函数、类上,还可以作用在属性 getter
和 setter
以及 lambda
表达式上。context(View)
val Int.dp get() = this.toFloat().dp// lambda 表达式
fun save(block: context(LogContextInterface) () ->
Unit)
最后我们来看一下,来自社区
Context Receivers
实践的案例,扩展 Json 工具类。fun json(build: JSONObject.() ->
Unit) = JSONObject().applybuild() context(JSONObject)
infix fun String.by(build: JSONObject.() ->
Unit) = put(this, JSONObject().build())context(JSONObject)
infix fun String.by(value: Any) = put(this, value)fun main()
val json = json
"name" by "Kotlin"
"age" by 10
"creator" by
"name" by "JetBrains"
"age" by "21"
总结
Context Receivers
提供一个基本的约束,可以在指定范围内,通过组合的方式实现更多的功能Context Receivers
可以作用在扩展函数、普通函数、类、属性getter
和setter
、lambda
表达式Context Receivers
允许在不需要继承的情况,通过组合的方式,组织上下文,将系统或者第三方类组合在一起,实现更多的功能- 通过
context()
关键字声明,在context()
关键字括号中,声明上下文接收者类型的列表,多个类型用逗号分隔 - 如果大量使用
this
关键字会导致可读性变差,我们可以通过接口隔离的方式来解决这个问题 - 当我们重写带有上下文接受者的函数时,必须声明为相同类型的上下文接受者
在看
、 点赞
、 收藏
、 分享
给身边的朋友。真诚推荐你关注我,公众号:ByteCode ,持续分享硬核原创内容,Kotlin、Jetpack、性能优化、系统源码、算法及数据结构、动画、大厂面经。
近期必读热门文章
- Stack Overflow 上最热门的 10 个 Kotlin 问题
- Android 12 已来,你的 App 崩溃了吗?
- Google 宣布废弃 LiveData.observe 方法
- 【揭秘 Kotlin 1.6.20 重磅功能 Context Receivers】影响性能的 Kotlin 代码(一)
- 揭秘 Kotlin 中的 == 和 ===
推荐阅读
- 什么是智慧矿山(它能解决什么问题?)
- 基于IP地址和端口的安全策略
- ubuntu安装cudnn
- Cilium DSR (Dircet Server Return)
- Uncaught InvalidArgumentException: Please provide a valid cache path. in /apps/vendor/laravel/framew
- 新作!分布式系统韧性架构压舱石OpenChaos
- CA数字证书包含哪些内容(如何查看SSL证书信息?)
- (0基础学Linux系列)1.02 Linux基础
- NLP教程 - 神经机器翻译seq2seq与注意力机制