我是靠谱客的博主 怕黑花瓣,最近开发中收集的这篇文章主要介绍Kotlin - DSL一、使用到的Kotlin语言特性二、构建器模式三、集合四、缩小作用域 @DslMarker,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
data class Person(
var name: String? = null,
var age: Int? = null,
var address: Address? = null
)
data class Address(
var street: String? = null,
var number: Int? = null,
var city: String? = null
)
fun person(block: Person.() -> Unit): Person = Person().apply(block)
//提供Address实例创建的扩展函数(定义为扩展函数避免污染Person类)
fun Person.address(block: Address.() -> Unit) {
address = Address().apply(block)
}
val person = person {
name = "John"
age = 25
address {
street = "Main Street"
number = 42
city = "London"
}
}
一、使用到的Kotlin语言特性
扩展函数/属性 详见文章 | 为任意对象增加函数或属性,还能链式调用。(例如不用传参Context) |
中缀表达式 详见文章 | 让函数调用省略.(),实现类似于英语句子的结构增强可读性。 |
Lambda 详见文章 | 简写函数,传参使用。 |
高阶函数:函数形参可以传入Lambda,且最后一个参数是Lambd可以放在括号外面实现了大括号层级。(将对象创建及初始化逻辑封装成最后一个形参是lambda的高阶函数中) | |
带接收者的Lambda:大括号中对it的饮用变为可以省略掉的this,让成员函数/属性调用更简洁。 | |
invoke约定:把对象当作函数名一样调用函数,让函数变量像 对象调用函数 一样调用自身。 | |
内联函数 详见文章 | 大量的高阶函数会产生大量的匿名类,使用 inline 减少生成提升性能。(内联函数内部调用的函数必须是public的,对于protected声明的函数使用@PublishedApi即可解决) |
变长参数 详见文章 | 纵向一行写一个要添加的参数。 |
1.1 扩展函数/属性
//扩展函数
fun Int.days() = Period.ofDays(this)
fun Period.ago() = LocalDate.now() - this
//使用效果
val aa = 123.days().ago()
//扩展属性
val Int.days: Period
get() = Period.ofDays(this)
val Period.ago: LocalDate
get() = LocalDate.now() - this
//使用效果
val aa = 123.days.ago
1.2 中缀表达式
//举例一
object ago
infix fun Int.days(ago: ago) = LocalDate.now() - Period.ofDays(this)
//使用效果
val aa = 123 days ago
//举例二
object start
infix fun String.should(start: start): String = ""
infix fun String.with(str: String): String = ""
//使用效果
val aa = "kotlin" should start with "K"
1.3 Lambda特性
1.3.1 简写函数、传参使用
//简写函数
fun sum(x: Int, y: Int): Int {...}
val aa: (Int, Int) -> Int = { x, y -> ... }
//传参使用
fun show(block: (Int, Int) -> Int) {...}
show(aa)
1.3.2 高阶函数
fun layout(block: () -> Unit) {...}
fun button(str: String, block: () -> Unit) {...}
//使用
layout {
button("click"){
...
}
}
1.3.3 带接收者的Lambda
fun show(block: StringBuilder.() -> Unit) {}
//使用
show {
append("aaa")
reverse()
insert(1, "b")
}
1.3.4 invoke约定
class Dependencies{
fun compile(coordinate:String){
println("add $coordinate")
}
operator fun invoke(block:Dependencies.()->Unit){
block()
}
}
//使用效果
val dependencies = Dependencies()
//Gradle中用法
dependencies {
compile("com.android.support:appcompat-v7:27.0.1")
}
//等价于
dependencies.compile("com.android.support:appcompat-v7:27.0.1")
二、构建器模式
实际开发中,类中属性会有val限制后期改变,因此只能调用构造函数来创建对象。
data class Person(
val name: String,
val birthday: Date,
val address: Address?
)
data class Address(
val street: String,
val number: Int,
val city: String
)
class PersonBuilder {
//构建器中提供和Person相同的属性用来后期赋值(构建数据)
var name = ""
var birthday: String = ""
//这里使用的是String,提供更可读的方式传参
private var address: Address? = null //这里使用private修饰,后期就不能直接对该属性赋值,只能调用提供的address()函数创建对象来赋值
//将传参的String类型转为Date
private var _birthday: Date = Date().apply {
SimpleDateFormat("yyy-MM-dd").parse(birthday)
}
//提供Person实例的创建函数(Person类中的属性有val修饰,只能调用构造函数创建对象)
fun build(): Person = Person(name, _birthday, address)
//提供Address实例的创建函数
fun address(block: AddressBuilder.() -> Unit) {
address = AddressBuilder().apply(block).build()
}
}
class AddressBuilder {
var street: String = ""
var number: Int = 0
var city: String = ""
fun build(): Address = Address(street, number, city)
}
fun person(block: PersonBuilder.() -> Unit): Person = PersonBuilder().apply(block).build()
val person = person {
name = "John"
birthday = "2000-10-01"
address {
street = "Main Street"
number = 42
city = "London"
}
}
三、集合
当类中属性存在一个或多个值时(集合)。
3.1 并行添加
data class Person(
private val addresses: List<Address>
)
data class Address(
val street: String
)
class PersonBuilder {
private val addresses = mutableListOf<Address>()
fun address(block: AddressBuilder.() -> Unit) {
addresses.add(AddressBuilder().apply(block).build())
//往集合中添加新地址
}
fun build(): Person = Person(addresses.toList()) //将集合传递给构造
}
class AddressBuilder {
var street: String = ""
fun build(): Address = Address(street)
}
fun person(block: PersonBuilder.() -> Unit): Person = PersonBuilder().apply(block).build()
val person = person {
//一个人有多个地址(并行添加)
address {
street = "八一路"
}
address {
street = "人民路"
}
}
3.2 合并添加
data class Person(
private val addresses: List<Address>
)
data class Address(
val street: String
)
class PersonBuilder {
private val addresses = mutableListOf<Address>()
fun addresses(block: ADDRESS_LIST.() -> Unit) = addresses.addAll(ADDRESS_LIST().apply(block))
fun build(): Person = Person(addresses)
}
class AddressBuilder {
var street: String = ""
fun build(): Address = Address(street)
}
//自定义List类,全大写强调是一个辅助类,将在SDL中不可见,实现一个很好的结构。
class ADDRESS_LIST : ArrayList<Address>() {
fun address(block: AddressBuilder.() -> Unit) {
add(AddressBuilder().apply(block).build()) //往自身添加Address对象
}
}
fun person(block: PersonBuilder.() -> Unit): Person = PersonBuilder().apply(block).build()
val person = person {
//一个人有多个地址(合并添加)
addresses {
address {
street = "八一路"
}
address {
street = "人民路"
}
}
}
四、缩小作用域 @DslMarker
由于嵌套的原因,我们可以调用 Lambda 内部每个可用的隐式接收者的函数,因此最终可能不是得到预期的结果。Kotlin v1.1 开始可以通过 @DslMaker 注解来避免这种情况,这时编译器就知道哪些隐式接收者是同一个DSL的一部分,只允许调用最近层的接收者成员。(想要的话仍可以使用显示接收者 this@person.name="李四")
val person = person { //this:PersonBuilder
name = "张三"
addresses { //this:ADDRESS_LIST
address { //this:AddressBuilder
name = "李四"
}
}
}
println(person.name)
//打印:李四
//应用到一个自定义的注释类,然后注释那些DSL类
@DslMarker
annotation class PersonDSL
//除了类,把函数都加上
@PersonDSL
class PersonBuilder {...}
@PersonDSL
class AddressBuilder {...}
@PersonDSL
class ADDRESS_LIST : ArrayList<Address>() {...}
最后
以上就是怕黑花瓣为你收集整理的Kotlin - DSL一、使用到的Kotlin语言特性二、构建器模式三、集合四、缩小作用域 @DslMarker的全部内容,希望文章能够帮你解决Kotlin - DSL一、使用到的Kotlin语言特性二、构建器模式三、集合四、缩小作用域 @DslMarker所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复