文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

【Kotlin】DSL 领域特定语言 ( apply 标准库函数分析 | 普通匿名函数 | 扩展匿名函数 | 泛型扩展匿名函数 )

2023-08-30 19:47

关注

本章总结 : 读懂 apply 标准库函数

public inline fun <T> T.apply(block: T.() -> Unit): T

核心是其 block: T.() -> Unit 参数 , 这是 泛型扩展匿名函数 ;

泛型扩展匿名函数 T.() -> Unit 演变路径 :





一、DSL 领域特定语言



在 Kotlin 中 , 定义的 标准库函数 apply 函数 , 函数原型如下 :

public inline fun <T> T.apply(block: T.() -> Unit): T {    block()    return this}

其中接收参数 类型为 T.() -> Unit泛型扩展匿名函数 , 暴露 接收者函数特性 , 以便于 使用 Lambda 表达式 读取 和 配置 接收者对象 ;

    "123".apply {        println(this)    }

上述写法 是 一种 Kotlin 提供的 编程范式 , 该编程范式 暴露 接收者函数特性 , 以便于 使用 Lambda 表达式 读取 和 配置 接收者对象 ; , 借助该编程范式 , 可以写出 DSL 领域特定语言 ;





二、apply 标准库函数分析



apply 函数 中 支持 接收者对象隐式调用 ;


1、apply 函数展示


如下所示 : 调用 “123” 字符串 的 apply 扩展函数 , 在函数的闭包参数中 , this 就是 接收者 “123” 字符串 , 在该 Lambda 表达式中可以 直接调用字符串的方法 ;
因此 , 调用 println(this) 代码 , 打印 this 就是打印 “123” 字符串 ;

调用 length 就是 调用 this.length , 获取 “123” 字符串 的长度 ;

fun main() {    "123".apply {        println(this)                var strLen = length        println(strLen)    }}

2、apply 函数原型分析


函数原型

apply 函数原型如下 : 该函数定义在 Standard.kt 脚本中 , 是一个 泛型扩展函数 , 所有的类型都可以使用该扩展函数 ;

@kotlin.internal.InlineOnlypublic inline fun <T> T.apply(block: T.() -> Unit): T {    contract {        callsInPlace(block, InvocationKind.EXACTLY_ONCE)    }    block()    return this}

参数和返回值分析

在 apply 函数中 , 接收的 参数类型是 block: T.() -> Unit , 这是一个 Lambda 表达式 / 匿名函数 / 闭包 ,

该 Lambda 表达式 block 类型是 T.() -> Unit , 其 返回值是 Unit 类型 , 表示没有返回值 ;

最终为 泛型 T 定义的泛型扩展函数 为 fun T.apply(block: T.() -> Unit): T , 其 返回的是 T 类型 , 也就是 接收者 本身 ;


3、匿名函数类型分析


继续分析 apply 函数的 参数 T.() -> Unit 类型的 Lambda 表达式 block , 该 Lambda 表达式没有返回值 ,

函数类型是 (参数类型列表) -> 返回值类型 , 如 :

可参考 【Kotlin】Kotlin 函数总结 ( 具名函数 | 匿名函数 | Lambda 表达式 | 闭包 | 内联函数 | 函数引用 ) 博客进行理解 ;


如果泛型扩展函数是 :

fun <T> T.apply(block: () -> Unit): T

就很容易理解 , 去掉参数类型 T.() -> Unit 中的 T. , 上述函数接收的就是一个 参数为空 , 返回值为空 的 Lambda 表达式 ;


4、扩展函数回顾


在回忆下扩展函数 , 为现有的类定义扩展函数 , 如 : 为 String 定义扩展函数 ;

下面的代码中 , String.addStr为 String 类型添加一个 扩展函数 addStr ;

fun String.addStr(str: String): String {    println("this = $this, string = $str")    return this + str}fun main() {    println("123".addStr("abc"))}

参考 【Kotlin】扩展函数总结 ( 超类扩展函数 | 私有扩展函数 | 泛型扩展函数 | 扩展属性 | 定义扩展文件 | infix 关键字用法 | 重命名扩展函数 | Kotlin 标准库扩展函数 ) 博客就进行理解 ;


5、泛型扩展函数函数类型


为泛型添加扩展函数 称为 泛型扩展函数 ,格式为 :

fun <T> T.函数名(参数列表): T {函数体}

如 : 为泛型 T 添加扩展函数 addStr , 没有参数 , 没有返回值 , 即 返回 Unit 类型返回值 , 代码如下 :

fun <T> T.addStr(): Unit {//函数体}

该 泛型扩展函数 的 类型 就是 apply 函数的 Lambda 表达式参数类型 T.() -> Unit ;


参考 【Kotlin】扩展函数总结 ( 超类扩展函数 | 私有扩展函数 | 泛型扩展函数 | 扩展属性 | 定义扩展文件 | infix 关键字用法 | 重命名扩展函数 | Kotlin 标准库扩展函数 ) 博客就进行理解 ;


6、泛型扩展匿名函数


扩展函数 匿名函数 是可以组合的 ; 扩展函数也可以是匿名函数 , 匿名函数也可以是扩展函数 ;

T.() -> Unit 的 函数类型是 泛型扩展匿名函数 , 这是 为 泛型 定义的 扩展函数 , 并且该扩展函数 是 匿名函数 ;


匿名函数 对应的是 具名函数 , 与 扩展函数 对应的是 原有函数 , 与 泛型 对应的是 具体类型 , 因此 三者是可以任意组合的 ;


这个匿名函数类型 T.() -> Unit 叠了三层 BUFF ;


泛型扩展函数匿名函数 T.() -> Unit 演变路径 :


7、apply 标准库函数参数分析


再次回到 apply 标准库函数 , 分析其函数原型 :

public inline fun <T> T.apply(block: T.() -> Unit): T

该函数的参数是一个 Lambda 表达式 / 匿名函数 / 闭包 , 类型为 T.() -> Unit , 这是一个 泛型扩展匿名函数 类型 , 为 泛型 T 定义的扩展函数 , 同时 T 还是接收者类型 , 返回类型 ;


泛型扩展函数匿名函数 与 普通匿名函数 对比

泛型扩展函数类型的匿名函数 与 普通匿名函数 对比 : apply 函数 传入了 泛型扩展匿名函数 类型 T.() -> Unit 的参数 , 而不是传入一个普通的 匿名函数 () -> Unit ;


apply 函数参数不是泛型扩展函数类型的假设

如果要 在 不使用 泛型扩展函数 的 前提下 , 达到上述 在 Lambda 表达式中 通过 this 调用 接收者 的效果 , 那么就需要使用 普通类型的 匿名扩展函数 ;

如 : 要想在 String 类型的 apply 扩展函数 的 闭包参数 中 通过 this 来调用 接收者 , 此时就必须使用 如下形式的 标准库 函数 ;

public inline fun String.apply(block: String.() -> Unit): String

一旦写成上述的代码样式 , 只有 String 类型可以调用 apply 函数 , 其它类型就无法调用该函数了 ;





三、代码示例




1、自定义 apply 函数接收 普通匿名函数 参数


使用 this 关键字报错

代码示例 : 在下面的代码中 , apply 函数的 参数是 () -> Unit 类型 , 这是 普通的匿名函数 , 在该闭包中无法调用 this ;

public inline fun <T> T.apply(block: () -> Unit): T {    println("调用普通匿名函数")    block()    return this}fun main() {    "123".apply {        println(this)    }}

一旦调用 this , 在编译时就会报错 , 提示如下错误 :

'this' is not defined in this context

在这里插入图片描述


使用变量名调用外部变量

这种情况下 , 只能 在匿名函数中通过变量名 , 调用外部的变量 ;

代码示例 :

public inline fun <T> T.apply(block: () -> Unit): T {    println("调用普通匿名函数")    block()    return this}fun main() {    var str = "123"    str.apply {        println(str)    }}

执行结果 : 打印 this , 可以直接将接收者打印出来 ;

调用普通匿名函数123

在这里插入图片描述


2、自定义 apply 函数接收 扩展匿名函数 参数


代码示例 : 如果要 在匿名函数中使用 this 关键字访问接收者 , 那么必须将其定义为扩展函数 ;

public inline fun String.apply(block: String.() -> Unit): String {    println("调用扩展匿名函数")    block()    return this}fun main() {    var str = "123"    str.apply {        println(this)    }}

执行结果 :

调用扩展匿名函数123

在这里插入图片描述


3、自定义 apply 函数接收 泛型扩展匿名函数 参数


代码示例 : 在下面的代码中 , 自定义了 apply 函数 , 其接收 泛型扩展函数类型的匿名函数 参数 , 类型为 T.() -> Unit , 在调用时 , 可以在 apply 函数的 Lambda 表达式中使用 this 调用接收者 ;

public inline fun <T> T.apply(block: T.() -> Unit): T {    println("调用自定义泛型扩展函数")    block()    return this}fun main() {    "123".apply {        println(this)    }}

执行结果 : 打印 this , 可以直接将接收者打印出来 ;

调用自定义泛型扩展函数 :123

在这里插入图片描述

来源地址:https://blog.csdn.net/shulianghan/article/details/128759825

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-人工智能
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯