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

这篇具有很好参考价值的文章主要介绍了【Kotlin】DSL 领域特定语言 ( apply 标准库函数分析 | 普通匿名函数 | 扩展匿名函数 | 泛型扩展匿名函数 )。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


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

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

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

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

  • 普通匿名函数 : () -> Unit , 这个函数 参数 和 返回值 都为空 ;
  • 扩展匿名函数 : String.() -> Unit , 这个函数 是 为 具体的 String 类型定义的扩展函数 ;
  • 泛型扩展匿名函数 : 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 脚本中 , 是一个 泛型扩展函数 , 所有的类型都可以使用该扩展函数 ;

/**
 * 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 , 该 Lambda 表达式没有返回值 ,

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

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

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


如果泛型扩展函数是 :

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

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


4、扩展函数回顾


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

下面的代码中 , 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 类型 , 这是 普通的匿名函数 , 在该闭包中无法调用 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

【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 标准库函数分析 | 普通匿名函数 | 扩展匿名函数 | 泛型扩展匿名函数 )


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 标准库函数分析 | 普通匿名函数 | 扩展匿名函数 | 泛型扩展匿名函数 )


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 标准库函数分析 | 普通匿名函数 | 扩展匿名函数 | 泛型扩展匿名函数 )文章来源地址https://www.toymoban.com/news/detail-406024.html

到了这里,关于【Kotlin】DSL 领域特定语言 ( apply 标准库函数分析 | 普通匿名函数 | 扩展匿名函数 | 泛型扩展匿名函数 )的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • kotlin标准函数also

    在 Kotlin 中,also 是一个标准库函数,其作用是将一个对象作为参数传递给 Lambda 表达式,并返回该对象本身 。语法如下: 使用 also 可以使代码更加简洁,常用于一系列需要对同一个对象进行多次操作的场景。例如: 上述代码首先输出字符串的长度,然后将其赋值给 length 变

    2024年02月06日
    浏览(47)
  • 卷起来!Dr. LLaMA:通过生成数据增强改进特定领域 QA 中的小型语言模型,重点关注医学问答任务...

    大家好,最近突然发现了一篇在专门应用于医学领域的LLaMA,名为Dr.LLaMA(太卷了太卷了),就此来分享下该语言模型的构建方法和最终的性能情况。 论文 :Dr. LLaMA: Improving Small Language Models in Domain-Specific QA via Generative Data Augmentation 地址 :https://arxiv.org/pdf/2305.07804.pdf 代码 :

    2024年02月11日
    浏览(48)
  • R语言【base】——apply():在数组边距上应用函数

    Package  base  version 4.2.0 返回通过将函数应用于数组或矩阵的边距而获得的向量或数组或值列表。 参数【X】 :一个数组,包括一个矩阵。 参数【MARGIN】 :一个给出下标的向量,函数将作用于这个下标。例如,对于矩阵, 1 表示行, 2 表示列, c(1,2) 表示行和列。其中 参数【

    2024年02月03日
    浏览(41)
  • 使用R语言的apply函数和mean函数计算矩阵每列数据的均值

    使用R语言的apply函数和mean函数计算矩阵每列数据的均值 在R语言中,apply函数是一个非常有用的函数,它可以对矩阵或数据框的行或列进行操作。而mean函数则是用来计算向量或矩阵的均值。结合这两个函数,我们可以很方便地计算矩阵每列数据的均值。 下面我们来演示一下如

    2024年02月01日
    浏览(49)
  • kotlin 比较 let apply

    `let` 和 `apply` 是 Kotlin 标准库中的两个非常有用的函数,它们用于在代码中实现更简洁和可读的操作。它们通常在函数式编程和链式调用中使用,以简化代码并提高可维护性。下面是关于这两个函数的详细解释: `let` 函数是一个作用域函数,它接收一个对象作为参数,并在作

    2024年02月11日
    浏览(34)
  • Kotlin DSL教程:使用DSL构建HTML | Android开发

    本文详细介绍了如何在Android开发中使用Kotlin DSL(领域特定语言)构建HTML。包括定义接口,实现父类和子元素,以及实际使用示例。

    2024年02月07日
    浏览(48)
  • 使用 Kotlin DSL 编写网络爬虫

    本博文将会通过一个网络爬虫的例子,向你介绍 Kotlin 的基本用法和其简洁有力的 DSL。 按照维基百科的说法,DSL(domain-specific language) 是一种专注于某一特定应用领域的计算机语言。和我们常用的通用目的型语言(类如 C,Java,Python 等)相反,DSL 并不承诺可用来解决一切可计

    2024年03月26日
    浏览(50)
  • Gradle Kotlin DSL 打包带上依赖

    Gradle version Gradle 8.2.1 build.gradle.kts 参考 https://blog.csdn.net/setlilei/article/details/123173339

    2024年02月15日
    浏览(37)
  • 切底掌握Android中的Kotlin DSL

    在这篇文章中,我们将学习如何在您的 Android 项目中编写 Kotlin DSL。 这个文章会很长,所以花点时间,让我们一起来写你的 DSL。我们将讨论以下主题, 什么是简单英语中的 DSL? 您使用任何 DSL 吗? 为什么我们使用 DSL? 我们如何编写自己的 DSL 基本示例说明。 那么让我们开

    2024年02月10日
    浏览(47)
  • kotlin的let,with,run,apply,also,异同区别

    例如: 输出: null null 吃饭 zhang 19 工作 996,赚59760 结果1:59760 ---------- zhang 20 工作 996,赚59760 吃饭 结果2:kotlin.Unit ---------- 吃饭 zhang 21 工作 996,赚59760 结果3:Person@7530d0a ---------- 吃饭 zhang 22 工作 996,赚59760 结果4:Person@7530d0a ---------- kotlin常用表达式let,?:,as?,?.,!!_zhangphi

    2024年02月01日
    浏览(60)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包