首页 > 其他分享 >揭秘 Kotlin 1.6.20 重磅功能 Context Receivers

揭秘 Kotlin 1.6.20 重磅功能 Context Receivers

时间:2022-12-12 10:35:16浏览次数:42  
标签:1.6 20 Context Receivers LogContext context fun 函数

hi 大家好,我是 DHL。公众号:ByteCode ,专注分享有趣硬核原创内容,Kotlin、Jetpack、性能优化、系统源码、算法及数据结构、动画、大厂面经。

这篇文章我们一起来聊一下 ​​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 ${[email protected]}")
writeFile("write message in file ${[email protected]}")
}

正如你所见,在 ​​LogContext​​​ 和 ​​FileContext​​​ 中都有一个名为 ​​name​​​ 的变量,我们只能通过 ​​this@Lable​​​ 语句来访问,但是这样会引入一个新的问题,如果有大量的同名的变量或者函数,会导致 ​​this​​ 关键字分散到处都是,造成可读性很差。所以我们可以通过接口隔离的方式,来解决这个问题。

interface LogContextInterface{
val logContext:LogContext
}
interface FileContextInterface{
val fileContext:FileContext
}

context(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().apply { build() }

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、性能优化、系统源码、算法及数据结构、动画、大厂面经。



标签:1.6,20,Context,Receivers,LogContext,context,fun,函数
From: https://blog.51cto.com/u_13238266/5929129

相关文章

  • 电压电流信号转频率(脉冲)信号隔离转换器0-10V/0-20mA/4-20mA转0-5KHz/0-10KHz/1-5KHz
    主要特性«将直流电压或电流信号转换成单位脉冲信号。«精度等级:0.1级、0.2级。产品出厂前已检验校正,用户可以直接使用。« 国际标准信号输入:0-5V/0-10V/1-5V等电压信......
  • 2022 年中总结|迷失中成长的半年
    距离上次总结过去半年了,这半年经历了很多的事情,经历了外企、自由职业,经历了上海全套的疫情,经历了比较emo的时刻,所幸技术还是在稳步成长中,也写了第二本小册,关于ts类型体......
  • 启明智显分享:SSD201/202开发环境搭建
    SSD201/202开发环境搭建1、下载源码,并将源码拷贝到linux目录下。2、安装交叉编译链(1)解压gcc-arm-8.2-2018.08-x86_64-arm-linux-gnueabihf.tar.gztar-xvfgcc-arm......
  • 2023营销号视频自动生成器
    功能:自动生成文本,自动添加字幕,自动给视频配音(人声)软件工具在文末:在开始之前,你可以新建一个文件夹然后新建一个文本文件,输入文字打开软件选择文件夹目录,就可以生成......
  • 2023震撼来袭!AI全自动原创图文视频生成器
    文字转视频工具,自媒体同学专用!这个软件主要用于批量快速将文章转为短视频,全自动添加(字幕,配音讲解,相关素材),不需要你手动添加和查找素材,你只需要提供txt文案,剩下的软件......
  • NOIP 2022 游记
    这是我第二次打NOIP\(\space\)考前预感到会考图论的东西,所以提前几天就重点复习了各种tarjan、圆方树之类的东西。当然,还有我亲爱的树hash。不知道今年会考什么......
  • 2022-12-11 现阶段纳斯达克不测而测推演,还是那5种情况
            ......
  • 2022.12.12 凌晨4:15焦虑&失眠
    焦虑,失眠为什么会有人一边说着对不起一边伤害别人很无助,外面在刮风,好恐怖一整晚都强迫性检查手机消息严重影响生活了,回想这一年浑浑噩噩痛苦不堪仿佛被诅咒,被什么牵......
  • 「HAOI2015」数字串拆分
    「HAOI2015」数字串拆分定义\(f_s\)将\(s\)拆分成\(1\simm\)的数的和的方案数,\(g_s\)将\(s\)这个数字串分割成若干个数字(允许前导\(0\)),设它们的和为\(x\),那......
  • 200004 计算混凝土方量已知长宽高
    点击查看代码<?phpheader('Content-Type:text/html;charset=utf-8');define('ROOT',$_SERVER['DOCUMENT_ROOT']);includeROOT.'/assets/php/head.php';$tit='......