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

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

时间:2023-02-06 14:33:12浏览次数:63  
标签:函数 扩展 匿名 泛型 apply Unit


文章目录

  • ​​一、apply 标准库函数分析​​
  • ​​1、apply 函数展示​​
  • ​​2、apply 函数原型分析​​
  • ​​函数原型​​
  • ​​参数和返回值分析​​
  • ​​3、匿名函数类型分析​​
  • ​​4、扩展函数回顾​​
  • ​​5、泛型扩展函数函数类型​​
  • ​​6、泛型扩展匿名函数​​
  • ​​7、apply 标准库函数参数分析​​
  • ​​泛型扩展函数匿名函数 与 普通匿名函数 对比​​
  • ​​apply 函数参数不是泛型扩展函数类型的假设​​
  • ​​二、代码示例​​
  • ​​1、自定义 apply 函数接收 普通匿名函数 参数​​
  • ​​使用 this 关键字报错​​
  • ​​使用变量名调用外部变量​​
  • ​​2、自定义 apply 函数接收 扩展匿名函数 参数​​
  • ​​3、自定义 apply 函数接收 泛型扩展匿名函数 参数​​



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

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

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

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

  • 普通匿名函数 : () -> Unit , 这个函数 参数 和 返回值 都为空 ;
  • 扩展匿名函数 : String.() -> Unit , 这个函数 是 为 具体的 String 类型定义的扩展函数 ;
  • 泛型扩展匿名函数 : T.() -> Unit , 这个函数 是为 所有的类型 定义的 泛型扩展匿名函数 , 所有的类都可以调用该匿名函数 ;





一、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 脚本中 , 是一个 泛型扩展函数 , 所有的类型都可以使用该扩展函数 ;

/**
* Calls the specified function [block] with `this` value
* as its receiver and returns `this` value.
* 以' this '值作为接收者调用指定函数[block],并返回' this '值。
*
* For detailed usage information see the documentation for [scope functions]
* (https://kotlinlang.org/docs/reference/scope-functions.html#apply).
*/
@kotlin.internal.InlineOnly
public 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

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

  • () -> Unit 类型表示是 参数为空 , 返回值也为空的函数 ;
  • () -> String 类型表示是返回值类型为 String 类型的函数 ;
  • (Int) -> String 类型表示是 参数为 Int 类型 , 返回值类型为 String 类型的函数 ;

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


如果泛型扩展函数是 :

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

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



4、扩展函数回顾



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

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

/**
* 为 String 定义扩展函数, 拼接原字符串和扩展函数参数, 并将结果返回
*/
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 演变路径 :

  • 普通匿名函数 : () -> Unit , 这个函数 参数 和 返回值 都为空 ;
  • 扩展匿名函数 : String.() -> Unit , 这个函数 是 为 具体的 String 类型定义的扩展函数 ;
  • 泛型扩展匿名函数 : T.() -> Unit , 这个函数 是为 所有的类型 定义的 泛型扩展匿名函数 , 所有的类都可以调用该匿名函数 ;


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



再次回到 apply 标准库函数

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

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



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

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

  • 泛型扩展函数类型的匿名函数 : 传入的是 泛型扩展函数类型 T.() -> Unit 的匿名函数 , 在该 Lambda 表达式中 , 可以使用 this 关键字访问接收者 , 可以直接调用接收者的成员属性和成员方法 ;
  • 普通匿名函数 : 如果 传入的是 普通的 匿名函数 , 则在函数中 不能使用 this 关键字访问接收者 , 必须将 接收者 作为外部变量进行访问 ;


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

如果要 在 不使用 泛型扩展函数 的 前提下 , 达到上述 在 Lambda 表达式中 通过 this 调用 接收者 的效果

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

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

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






二、代码示例




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



使用 this 关键字报错

代码示例 : 在下面的代码中 , apply 函数的 参数是 () -> Unit 类型 , 这是 普通的匿名函数

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

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



使用变量名调用外部变量

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

代码示例 :

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

fun main() {
var str = "123"
str.apply {
println(str)
}
}

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

调用普通匿名函数
123

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



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

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



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

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



标签:函数,扩展,匿名,泛型,apply,Unit
From: https://blog.51cto.com/u_14202100/6039119

相关文章

  • AWS EMR实例组竞价实例扩展失败切换按需实例方法
     背景本文介绍如何解决EMR集群实例组模式下竞价实例请求失败后无法切换到按需实例问题,主要思路是通过CloudWatch+Lambda实现在EMR集群竞价实例请求失败情况下补充制定数量......
  • 强化学习 6 ——价值函数逼近 (VFA)
    上篇文章​​强化学习——时序差分(TD)控制算法Sarsa和Q-Learning​​我们主要介绍了Sarsa和Q-Learning两种时序差分控制算法,在这两种算法内部都要维护一张Q表格......
  • web之命令执行常见函数------2023.2.6
     system()函数作用:将字符串作为OS命令执行,自带输出功能。格式:stringsystem(string$command[,int&$return_var])//$command为执行的命令,&return_var可选,用来......
  • 一次项目中Thinkphp绕过禁用函数的实战记录
    目录前言file_put_contentspcntl_execLD_PRELOAD劫持总结 前言在一次渗透测试中,手工找了许久没发现什么东西,抱着尝试的心情打开了xray果然xray还是挺给力的,一......
  • Hive窗口函数中range和rows的区别
    说明聊到hive,就少不了灵活的开窗函数,今天介绍下开窗函数中over子句内部经常会用到的rows和range的用法;数据准备createtabletemp_id_0116(idint)stor......
  • 六、python——函数
    pizza.pydefmake_pizzas(size,*toppings):"""概述要制作的比萨"""print('size:'+size)print(toppings)deftest_one():print('one')deft......
  • 莫比乌斯函数
    唯一分解定理\[n=\prod^{s}_{i=1}p_{i}^{a_i}=p_{1}^{a_1}p_{2}^{a_2}···p_{s}^{a_s}\]莫比乌斯函数的定义\[\mu(n)=\left\{\begin{aligned}&1&&n......
  • 前缀函数与 KMP
    前缀函数概述前缀函数\(\pi_i\)为\(s_{1\dotsi}\)的真前后缀最大相同长度。这里的所有\(s\)下标从\(1\)开始,长度为\(n\)。实现原理首先肯定能想到......
  • python中的lambda函数用法
    python中的lambda函数用法 例1:传入多个参数的lambda函数defsum(x,y):returnx+y用lambda来实现: p=lambdax,y:x+yprint(p(4,6))例2:传入一个参......
  • APIView+ModelSerializer+Resposne、基于GenericAPIView+5个视图扩展类、drf之响应、d
    上节课回顾#1序列化类的常用字段 -CharField。。。。-ListField-DictField#2字段参数 -max_length。。。-min_value。。。-required,defa......