Kotlin对整洁语法的支持
常规语法 | 整洁语法 | 用到的功能 |
StringUtil.capitalize(s) | s.capitalize() | 扩展函数 |
1.to(“one”) | 1 to “one” | 中缀调用 |
set.add(2) | set+=1 | 运算符重载 |
map.get(“key”) | map[“key”] | get方法约定 |
file.use({f->f.read() } ) | file.use{it.read()} | 括号外的lambda |
sb.append(“yes”) sb.append(“no”) | with(sb){ append(“yes”) \n append(“no”)} | 带接收者的lambda |
DSL语言分类及特点
通用编程语言: 有一系列足够完善的能力来解决几乎所有能被计算机解决的问题
领域特定语言:专注在特定的任务或者领域上,并放弃的与该领域无关的功能 (外部DSL),而领域特定语言分为外部DSL与内部DSL
DSL更趋向声明式 :语言包括有命令式和声明式写法 ,命令式语言描述执行操作所需步骤的确切序列,每个操作实现都被独立化了,而声明式描述了想要的结果并将执行细节留给解释它的引擎,通常让执行更有效率。
外部DSL语言:
声明式写法,很难与通用编程语言的宿主应用程序结合起来使用,外部DSL语言自己的语法并不能直接嵌套使用。
内部DSL: 是使用通用编程语言编写程序的一部分,包含了外部DSL声明式和通用语言的语法优点
DSL 的结构
DSL与普通的api之间并没有明确的边界,但是DSL经常会出现一个通常在其他api中不存在的特征:结构或文法
结构:api的前后调用在一个大的结构块中。中间需要维护调用的上下文信息
命令式api:前后调用没有内在的结构,也不需要维护上下文。
比如常见的DSL结构文法:
构建结构化的API:DSL中带接收者的lambda
先来看一个表达式里面的知识点
普通函数类型如何转换成扩展函数类型
- 函数参数类型 = 函数类型(builderAction:( StringBuilder)-> Unit),将函数类型转换为扩展
函数类型 - 将函数类型签名中的一个参数(类型)移到括号前面,并用一个.将它与其他的( 参
数)类型分隔开。用 StringBuilder.() -> Unit 代替(StringBuilder ) -> Unit - 这个特殊的类型( StringBuilder )就叫作接收者类型,传递给 lambda 的这个类型的值就叫作接收者对象
函数类型转换成扩展函数类型
扩展函数类型表达式
String.(Int,Int)->Unit
一个扩展函数类型,接收者类型是 String,两个参数类型是 Int ,返回类型是 Unit
当你将一个普通函数类型转换为扩展函数类型时,其调用方式也发生了变化 。 像调用一个扩展函数那样调用 lambda,而不是将对象作为参数传递给 lambda。在 使用普通 lambda 时,我们使用这样的语法将一个 StringBuilder 实例作为 参 数给它 :builderActi on (sb )。但当 你将它改成带接收者的 lambda 时,代码 就变成了 sb .builderAction () 。再次重 申,这里的 builderAction 并不是 StringBuilder 类的方法,它是一个函数类型的参数,但可以用调用扩展函数一 样的语法调用它。
使用 invoke约定构建更灵活的代码块嵌套
invoke约定允许把自定义类型的对象当做函数一样调用。(函数类型对象可以作为函数调用),定义invoke需要使用operator 修饰符进行修饰。入参和返回类型可以是任意受系统支持的类型:
invoke约定调用示例:
函数类型对象调用示例
Lambda,除非是内联的,都是被编译成实现了函数式接口(Functionl 等)的类,而这些接
口定义了具有对应数量参数的 invoke 方法,如上 function1:(Int)->Boolean编译成字节码后会被系统使用function1函数进行替换。
使用invoke约定定义,可以将lambda函数体中抽取的方法的作用域尽可能的缩小,能够在不耦合外部逻辑的情况下实现代码解耦。
例子:
如上将 (Issue)->Boolean 最为基类。并复写invoke方法,并且定义了Issue.import( )扩展函数,将对比的代码抽取到mportantIssuesPredicate 函数类型对象中。如果不使用nvoke 约定,就需要将判断代码嵌套到for函数中。
DSL中的"invoke" 约定:在Gradle中的声明依赖
下面是开发中常见的依赖配置:
如上: dependencies 对象是 DependencyHandler类型。
看到main()函数中写法和build.gradle 中脚本写法一致。通过这样设计的好处,有多个依赖就可以使用嵌套结构,一个依赖可以使用扁平调用结构
实践中Dsl用法
- 中缀调用链接起来:测试框架中的“should”
infix fun T.should(matcher:Matcher) =matcher.test(this)
infix中缀标示,可以不用写. 进行调用。
- 在基本类型上定义扩展:处理日期
val yesterday =1.days.ago
val tomorrow =1.days.fromNow
kxDate
- 为sql设计的内部Dsl
- Anko :动态创建Android Ui
总结:
内部Dsl 是一种Api设计模式,借助多个方法调用组成的结构,可以使用这种模式来构建更表意的Api,带接收者lambda采用嵌套结构重新定义函数体中的方法如何解析。成员扩展函数依然收到容器的限制。与普通扩展函数具有不同的使用的场景。用作参数的带接收者的lambda,其类型是扩展函数类型,并且这个调用函数在调用lambda时会为它提供一个接收者实例。
1.关于Kotlin扩展函数与lambda的上下文
2.标识符、修饰符、关键字解释