1.变量
val:不可变的变量
var:可变的变量
1 | fun main(){ |
2.函数
1 | fun largerNumber(num1: Int, num2: Int) = max(num1, num2) |
3.函数的执行语句
🔹 if语句
if具有返回值,返回值就是if语句中最后一行代码的返回值
1 | fun largerNumber(num1: Int, num2: Int) = if (num1>num2)num1 else num2 |
🔹 when条件语句(类似于switch)
和if一样也是自带返回值
1 | fun getScore(name: String) = when (name) { |
1 | fun main() { |
1 | fun getScore(name: String) = when { |
循环语句
1 | val range = 0..10 // [0,10]的区间 |
1 | val range = 0 until 10 // [0,10)的区间 |
1 | fun main(){ |
3.类与对象
1 | class Person(var name: String, var age: Int) { |
1 | fun main(){ |
4.继承与构造函数
在Kotlin中任何一个非抽象类默认都是不可被继承的
1 | open class Person(var name: String, var age: Int) { // 加上open就可以被继承 |
1 | class Student : Person() { // 继承用冒号 |
主构造函数
1 | class Student(name: String, age: Int) : Person(name, age){ |
5.接口
1 | interface Study { |
1 | class Student(name: String, age: Int) : Person(name, age), Study{ // 调用接口用逗号 |
5.数据类
1 | data class Cellphone(val brand: String, var price: Double) // 数据类用data关键字 |
1 | fun main(){ |
6.单例类(在全局只有一个实例)
1 | object Singleton { // 单例类用odject关键字 |
1 | Singleton.singletonTest() // 调用 |
7.Lambda编程
🔹常见使用场景总览
| 场景 | 描述 | 示例 |
|---|---|---|
| 1️⃣ 集合操作 | map、filter、forEach 等 |
list.filter { it.isNotEmpty() } |
| 2️⃣ 事件监听 | 点击、输入等 | button.setOnClickListener { showToast() } |
| 3️⃣ 函数参数 | 高阶函数传入行为 | repeat(5) { println("Hello") } |
| 4️⃣ 线程/协程 | 简化异步任务 | Thread { doWork() }.start() |
| 5️⃣ 自定义回调 | 封装逻辑,暴露接口 | fetchData { result -> println(result) } |
🔹 集合的创建与遍历
List
1 | val list = ListOf("Apple", "Banana", "Orange", "Pear", "Grape") // 不可变集合 |
1 | val list = mutableListOf("Apple", "Banana", "Orange", "Pear", "Grape") // 可变集合 |
Set
1 | val list = SetOf("Apple", "Banana", "Orange", "Pear", "Grape") // 不包含重复元素 |
Map
1 | val map = mapOf("Apple" to 1, "Banana" to 2, "Orange" to 3,"Pear" to 4, "Grape" to 5) |
🔹 集合的函数API
Lambda表达式的语法结构 : {参数名1: 参数类型, 参数名2: 参数类型 -> 函数体}
1 | // 1. 定义一个具名 lambda 变量,然后传入 maxBy 函数 |
map函数是最常用的一种函数API
1 | fun main() { |
filter函数用于过滤集合中的数据
1 | fun main() { |
any和all函数
1 | val list = mutableListOf("Apple", "Banana", "Orange", "Pear", "Grape") |
🔹 Java函数式API的使用
1 | Thread{ |
8.空指针检查
Kotlin默认所有参数和变量不能为空
**? ?. ?: **
1 | fun doStudy(study: Study?) { // 加上?可为空 |
1 | fun doStudy(study: Study?) { |
1 | val c = a ?: b // 左边为空就返回右边的结果 |
1 | fun fetTextLength(text: String?) = text?.length ?: 0 |
非空断言工具 : !!
1 | val upperCase = content!!.toUpperCase() // 不用空指针检查 |
let函数
1 | fun doStudy(study: Study?) { |
9.函数的参数默认值
1 | fun printParams(num: Int = 100, str: String){ // 不传参数时返回默认值 |
10.标准函数with,run和apply
🔹 with
-
作用:
用于对传入的对象执行一系列操作,省去重复引用该对象的麻烦。
它并不是扩展函数,而是一个顶层函数。 -
返回值:
返回 lambda 表达式中最后一行代码的执行结果。 -
语法:
1
2
3
4with(receiver) {
// 在这里可以直接访问 receiver 的成员(属性和方法)
// 最后一行的表达式将作为返回值
} -
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 假设有一个 Person 类
data class Person(var name: String, var age: Int)
fun main() {
val person = Person("Alice", 25)
// 使用 with 对 person 对象进行操作
val info = with(person) {
// 可以直接访问 person 的属性和方法
println("Name: $name")
println("Age: $age")
// 返回一个结果字符串
"$name is $age years old."
}
println(info) // 输出: "Alice is 25 years old."
}
🔹 run
-
作用:
run 既可以作为扩展函数,也可以作为普通函数。作为扩展函数时,它允许在对象上直接调用,从而在 lambda 表达式内使用该对象的上下文。 -
返回值:
返回 lambda 表达式中最后一行代码的执行结果。 -
语法:
1
2
3
4receiver.run {
// 在 lambda 中,this 指向 receiver,可以直接访问其成员
// 最后一行的表达式的结果将作为返回值
} -
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13// 继续使用 Person 类
fun main() {
val person = Person("Alice", 25)
// 使用 run 在 person 对象上直接调用
val result = person.run {
// 在这里可以直接引用 person 的属性和方法
println("Name: $name")
println("Age: $age")
// 返回一个字符串作为结果
"$name, age $age"
}
println(result) // 输出: "Alice, age 25"
}
🔹 apply
-
作用:
apply 主要用于对象的初始化或配置。它以扩展函数的形式存在,可以对对象的属性进行设置,而无需多次引用对象本身。 -
返回值:
返回调用对象本身,而不是 lambda 中最后一行表达式的结果。 -
语法:
1
2
3
4receiver.apply {
// 在 lambda 中,this 指向 receiver
// 对对象进行一系列配置或初始化操作
} -
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12// 继续使用 Person 类
fun main() {
// 使用 apply 对 Person 对象进行初始化
val newPerson = Person("Bob", 30).apply {
// 这里可以设置对象属性或者调用对象方法
println("Initializing person: $name, $age")
// 可以进行链式调用
age += 1 // 修改属性
}
// apply 返回的是修改后的 newPerson 对象
println(newPerson) // 输出: Person(name=Bob, age=31)
}
🔹 总结
- with:
- 需要传入对象作为参数。
- 在 lambda 中操作对象,返回值为最后一行表达式的结果。
- 适合对对象进行一系列操作后需要返回计算结果的场景。
- run:
- 作为扩展函数使用,可在对象上直接调用。
- 同样返回 lambda 的最后一行表达式结果。
- 常用于执行一段代码块并返回一个计算结果。
- apply:
- 主要用于对象的初始化与配置。
- 返回调用对象本身,便于链式调用。
- 适合在创建对象后需要对对象属性进行设置的场景。
11.定义静态方法(不需要实例就可以调用的方法)
🔹 运用注解
1 | class Util { |
1 | Util.doAction2() // 调用静态方法 |
🔹 运用顶层方法
创建一个Kotlin文件(File),在里面写上静态方法,后面可以直接调用
12.变量延迟初始化
使其不用进行判空处理,但注意要记得初始化
1 | // private var adapter: MsgAdapter?=null |
13.使用密封类优化代码
选择时可以不加 else
1 | /* interface Result |
14.扩展函数
扩展函数是 Kotlin 中的一种语言特性,它允许你在不继承或修改原有类源码的情况下,为已有的类添加新的功能。
语法结构
1 | fun ClassName.methodName(param1: Int, param2: Int): Int { |
创建一个String.kt 文件
1 | fun String.lettersCount(): int { |
1 | val count = "AVSVGS_+".lettersCount() |
15.运算符重载
运算符重载允许你为自定义类型定义标准运算符(如 +、-、*、/ 等)的行为,从而使得这些类型的对象可以像基本数据类型一样使用运算符。运算符重载通常通过定义特定名称的函数来实现,在 Kotlin 中需要使用 operator 修饰符。例如,为了重载加号运算符,你可以为一个类定义一个 plus 函数:
1 | class Money(val value: Int) { |
1 | val money1 = Money(5) |
16.高阶函数
在 Kotlin 中,高阶函数(Higher-Order Function)是指接受函数作为参数或者将函数作为返回值的函数。由于 Kotlin 将函数视为一等公民(first-class citizens),这使得你可以灵活地处理函数,实现更高的抽象和代码复用。下面详细介绍 Kotlin 中高阶函数的相关概念和用法。
🔹 接受函数作为参数
定义一个高阶函数,该函数接受一个函数作为参数:
1 | // 定义一个高阶函数,接受两个整数和一个函数作为参数 |
在这个例子中,calculate 函数通过参数 operation 接受了一个 lambda 表达式,用于定义具体的计算操作。
🔹 将函数作为返回值
一个函数可以返回另一个函数,例如:
1 | // 定义一个函数,返回一个加法函数 |
这里 getAdder 返回一个 lambda 表达式,该表达式实现了两个整数相加的功能。
17.内联函数
在 Kotlin 中,使用 inline 关键字声明的函数称为内联函数。编译器在编译过程中会将内联函数的代码直接插入到调用处,而不是生成独立的函数调用。这样做可以避免高阶函数传递 lambda 表达式时产生的对象分配和调用开销。
1 | // 定义一个内联函数,接受两个整数和一个 lambda 表达式作为操作 |
🔹 noinline
作用与场景
- 默认行为:在内联函数中,所有的 lambda 参数默认都会被内联(即直接替换到调用处),从而减少额外的函数调用开销。
- 使用场景:有时需要将某个 lambda 参数当作普通对象传递,比如存储到变量中、放入集合、或者传递给其它非内联函数等,这时就不能将它内联,否则会破坏其“对象”特性。
- noinline 关键字:标记在 lambda 参数前,表示该参数在调用过程中不内联,编译器不会将该 lambda 展开到调用处。
1 | // 定义一个内联函数,其中 block1 会内联,block2 不会内联 |
🔹 crossinline
作用与场景
- 非局部返回问题:在内联函数中,lambda 参数默认允许使用非局部返回(non-local return),即在 lambda 中使用
return可以直接返回到外层调用函数。但当 lambda 被传递到其他执行环境中(比如异步回调或其它嵌套调用)时,非局部返回可能会导致逻辑混乱或无法预料的行为。 - 使用场景:如果希望 lambda 参数仍然内联(从而获得性能优势),但又不允许其使用非局部返回,就可以使用 crossinline。
- crossinline 关键字:标记在 lambda 参数前,强制该 lambda 内的
return只能用于返回 lambda 本身,不能用于返回外层函数。
1 | // 定义一个内联函数,其中 block 参数被标记为 crossinline |
18.高阶函数的运用
简化SharedPreferences
1 | getSharedPreferences("data",Context.MODE_PRIVATE).edit { |
简化ContentValues
1 | val values =contentValuesOf( |
19.泛型的基本用法
泛型允许我们在不指定具体类型的情况下进行编程
定义泛型类
1 | class MyClass<T> { |
调用泛型类
1 | val myClass = MyClass<Int>() |
定义一个泛型方法
1 | class MyClass { |
调用一个泛型方法
1 | val myClass = MyClass() |
对泛型进行类型限制
1 | class MyClass { |
20.类
概念:
类委托是 Kotlin 提供的一种设计模式,可以将接口的实现委托给另一个对象。也就是说,当一个类需要实现某个接口时,可以将接口中的部分或全部方法调用转交给另一个对象处理,从而实现代码复用和职责分离。
用途:
- 简化代码实现,避免重复编写接口方法。
- 通过组合而非继承来扩展类的功能。
- 动态改变行为,增强灵活性。
1 | // 定义一个接口 |
在上面的例子中,Derived 类通过 : Base by b 将 Base 接口的实现委托给了 baseImpl 对象,因此调用 derived.printMessage() 实际上调用的是 baseImpl 中的方法实现。
21.委托属性
概念:
委托属性允许你将属性的 getter 和 setter 的逻辑交由另一个对象处理,这个对象称为“委托”。Kotlin 标准库提供了一些常用的委托,如 lazy、observable、vetoable 等,也可以自定义委托。
用途:
- 实现延迟初始化(如
lazy委托)。 - 属性变更监听(如
observable委托)。 - 属性值验证、缓存等其他特殊逻辑。
- 简化重复代码,提高代码复用性。
-
延迟初始化(Lazy Delegation)
1
2
3
4
5
6
7
8
9
10val lazyValue: String by lazy {
println("Computed!")
"Hello, Lazy Delegation!"
}
fun main() {
println("Before accessing lazyValue")
println(lazyValue) // 第一次访问时,计算并打印 "Computed!" 和 "Hello, Lazy Delegation!"
println(lazyValue) // 后续访问直接返回已计算的值
} -
可观察属性(Observable Delegation)
1
2
3
4
5
6
7
8var observedValue: Int by Delegates.observable(0) { property, oldValue, newValue ->
println("属性 ${property.name} 从 $oldValue 变为 $newValue")
}
fun main() {
observedValue = 10 // 输出:属性 observedValue 从 0 变为 10
observedValue = 20 // 输出:属性 observedValue 从 10 变为 20
}
在这两个例子中,委托属性分别将属性初始化逻辑和修改监听逻辑分离到专门的委托对象中,从而让属性本身的声明更加简洁。
22.infix函数构造更可读的语法(改名字)
创建一个beginsWith函数,使其具有startsWith函数的功能
1 | infix fun String.beginsWith(prefix: String) = startsWith(prefix) |
创建一个has函数,使其具有contains函数的功能
1 | infix fun <T> Collection<T>.has(element: T) = contains(element) |
23.对泛型进行实化
在 Kotlin 的内联函数(inline function) 中,我们可以使用 reified 关键字实化泛型,使得泛型参数在运行时仍然可以被访问。
🔹 使用 reified 实化泛型
1 | inline fun <reified T> printType() { |
泛型 T 仍然存在于运行时,可以用 T::class.simpleName 获取类型名称。
🔹 为什么要使用 reified?
(1) 运行时获取泛型类型
1 | inline fun <reified T> isType(value: Any): Boolean { |
普通泛型无法做到 is T 判断,而 reified 泛型可以!
(2) 在泛型函数中创建对象
使用 reified 可以直接创建对象,而普通泛型无法做到:
1 | inline fun <reified T> createInstance(): T? { |
可以直接实例化泛型类型 T,避免手动传递 Class<T>。
(3) 简化 Class 作为参数的 API
在 Java 中,我们通常需要显式传递 Class<T>:
1 | public <T> T fromJson(String json, Class<T> clazz) { |
在 Kotlin 中,reified 让 Class<T> 变得不再必要:
1 | inline fun <reified T> Gson.fromJson(json: String): T { |
可以直接使用 T::class.java,无需手动传递 Class<T>!
(4) 简化 Activity 的启动
1 | inline fun <reified T> startActivity(context: Context, block: Intent.() -> Unit) { |
1 | startActivity<TestActivity>(context) { |
24.泛型的协变与逆变
在 Kotlin 中,泛型可以是 协变(covariant) 或 逆变(contravariant),用于控制泛型类型参数在继承关系中的行为,以保证类型安全。
🔹 什么是协变?
协变允许子类型泛型对象赋值给父类型泛型对象,但泛型类型参数只能作为返回值,不能作为参数。
规则:Producer<T>(生产者)—— T 只能被返回,不可被消费
- 关键字:
out - 适用于只“生产”数据的场景
- 子类泛型对象可以赋值给父类泛型对象
🔹 举例
假设我们有一个 Animal 类和 Dog 类:
1 | open class Animal |
定义一个协变接口:
1 | interface Producer<out T> { // 使用 `out` 关键字 |
可以将 Producer<Dog> 赋值给 Producer<Animal>
1 | class DogProducer : Producer<Dog> { |
为什么可以赋值?
Producer<Dog>只能 返回Dog,而Producer<Animal>也应该能返回Animal- 由于
Dog是Animal的子类,因此Dog的生产者也可以充当Animal的生产者
错误:协变泛型不能用作输入参数
1 | interface Producer<out T> { |
🔹 适用场景
- 生产者模式(Producer),例如:
List<T>(List<Dog>可以赋值给List<Animal>)Sequence<T>
🔹 什么是逆变?
逆变允许父类型泛型对象赋值给子类型泛型对象,但泛型类型参数只能作为输入参数,不能作为返回值。
规则:Consumer<T>(消费者)—— T 只能被消费,不可被返回
- 关键字:
in - 适用于只“消费”数据的场景
- 父类泛型对象可以赋值给子类泛型对象
🔹 举例
定义一个逆变接口:
1 | interface Consumer<in T> { // 使用 `in` 关键字 |
可以将 Consumer<Animal> 赋值给 Consumer<Dog>
1 | class AnimalConsumer : Consumer<Animal> { |
为什么可以赋值?
Consumer<Animal>需要消费Animal或其子类Consumer<Dog>也需要消费Dog- 由于
Dog是Animal的子类,因此Consumer<Animal>也可以用于Consumer<Dog>
错误:逆变泛型不能用作返回值
1 | interface Consumer<in T> { |
🔹 适用场景
- 消费者模式(Consumer),例如:
Comparator<T>(比较器)Function<T, R>(输入T)
| 对比项 | 协变 (out) |
逆变 (in) |
|---|---|---|
| 关键字 | out |
in |
| 可以作为返回值? | 可以 | 不可以 |
| 可以作为输入参数? | 不可以 | 可以 |
| 泛型类型关系 | Producer<Dog> 可以赋值给 Producer<Animal> |
Consumer<Animal> 可以赋值给 Consumer<Dog> |
| 适用场景 | 生产者(Producer<T>) |
消费者(Consumer<T>) |
| 特性 | 协变(out) |
逆变(in) |
|---|---|---|
| 作用 | 生产者,返回 T |
消费者,接收 T |
| 关键字 | out |
in |
| 可以作为返回值? | 可以 | 不可以 |
| 可以作为输入参数? | 不可以 | 可以 |
| 子类型赋值关系 | Producer<Dog> 可以赋值给 Producer<Animal> |
Consumer<Animal> 可以赋值给 Consumer<Dog> |
| 使用场景 | List<T>、Sequence<T> |
Comparator<T>、Function<T, R> |
记住:
- “生产者用
out,消费者用in” List<T>是out,Comparator<T>是inMutableList<T>不能用out或in
25.协程
Kotlin 协程(Coroutines)是 Kotlin 提供的一种用于简化异步编程和并发编程的工具。它能够高效地管理多任务,并且与线程相比,协程的开销小、运行速度快。协程让我们能够以一种顺序的方式编写异步代码,避免了回调地狱、线程阻塞等问题。
🔹 什么是协程
协程是轻量级的线程,它们能够在单一线程中并发运行,且不会像传统的线程那样消耗过多的资源。协程本质上是挂起函数,它允许在执行过程中暂停,并且能够恢复执行。
🔹协程的基本概念
- 挂起函数(Suspending Function):是协程的核心,允许协程暂停并恢复执行。挂起函数通常以
suspend关键字声明。 - 协程作用域(Coroutine Scope):协程在一个特定的作用域内运行,确保协程的生命周期与作用域相关联。协程作用域的任务可以并行执行,并且在作用域结束时,所有的协程都会被取消。
- 协程调度器(Dispatcher):负责控制协程在哪个线程上执行。例如,
Dispatchers.Main表示协程在主线程执行,Dispatchers.IO用于 I/O 操作。 - 挂起点(Suspension Point):当协程执行到某个挂起函数时,它会在该点暂停,挂起函数可能会等待某些异步操作完成后恢复执行。
🔹 使用协程的基本步骤
(1)添加协程依赖
首先,你需要在 build.gradle 文件中添加协程库依赖:
1 | // 协程基础库 |
(2)启动协程
你可以在不同的协程作用域内启动协程。GlobalScope 是最简单的方式,但在 Android 中不推荐使用。推荐在 ViewModel 中使用 viewModelScope,或者在 Activity 和 Fragment 中使用 lifecycleScope。
使用 GlobalScope 启动一个协程(不推荐在 UI 线程中使用):
1 | fun main() { |
launch:启动一个协程,返回一个Job对象,用于管理协程的生命周期。delay:挂起当前协程,但不会阻塞线程,其他协程可以继续运行。
(3)挂起函数
协程中的挂起函数是协程的核心,可以暂停协程的执行,并在异步操作完成后恢复。
1 | fun main() = runBlocking { // runBlocking 用于在主线程启动协程 |
runBlocking:用于启动一个协程并阻塞当前线程,通常在顶层函数中使用来启动协程。suspend:修饰挂起函数,将该该函数挂起使其可以在runBlocking协程作用域中调用。delay:不会阻塞线程,它只会挂起当前协程。
(4)使用不同的协程作用域
在 Android 开发中,我们通常会使用 lifecycleScope 和 viewModelScope 来启动协程。
在 Activity 或 Fragment 中使用 lifecycleScope:
1 | lifecycleScope.launch { |
lifecycleScope:与 Activity 或 Fragment 的生命周期绑定,协程会在生命周期结束时自动取消。
在 ViewModel 中使用 viewModelScope:
1 | fun fetchData() { |
viewModelScope:与 ViewModel 的生命周期绑定,当 ViewModel 被清除时,所有协程会自动取消。
(5)使用 async 和 await 进行并行操作
async 和 await 用于同时启动多个协程,并在完成时返回结果。async 会返回一个 Deferred 对象,await 用来获取返回值。
1 | fun main() = runBlocking { |
async:启动一个并发协程并返回Deferred对象。await:等待协程的结果。
(6)使用 withContext 切换协程上下文
有时你需要切换协程执行的线程上下文,例如在网络请求后将结果更新到 UI 线程上。
1 | fun main() = runBlocking { |
withContext(Dispatchers.IO):切换到 I/O 线程来执行耗时操作。withContext(Dispatchers.Main):切换到主线程(UI 线程)来更新 UI。
🔹协程异常处理
Kotlin 协程还提供了异常处理机制。你可以使用 try-catch 块来捕获协程内部的异常。
1 | fun main() = runBlocking { |
🔹协程的取消
协程支持取消操作。通过调用 Job.cancel() 方法可以取消协程,cancel() 会使协程挂起并且不会继续执行。
1 | val job = GlobalScope.launch { |
26.DSL构建专有的语法结构
DSL 是专为特定领域(如数据库查询、配置文件、UI布局等)设计的编程语言或语言的一个子集。Kotlin 提供了丰富的支持,帮助开发者用 Kotlin 代码构建出自己的 DSL 语言结构,以使得代码更加易读和易写。
- 扩展函数:你可以通过扩展函数给现有类添加自定义功能,从而实现新的语法结构。
- 高阶函数:Kotlin 允许将函数作为参数传递给其他函数,这使得定义和构建 DSL 成为可能。
- Lambda 表达式:你可以通过 lambda 表达式使代码更加简洁,形成类似声明式的语法。
- 智能类型推断:这使得你无需显式声明类型,语法更清晰,推导更自然。
🔹Lambda with Receiver(接收者 lambda)
接收者 lambda 允许在 lambda 中访问外部对象的成员,而不需要显式的引用对象。通过这种方式,你可以通过一个语法块来访问和操作对象,类似于嵌套调用的结构。
1 | class DSL { |
在这个例子中,init 是一个扩展函数 DSL.() -> Unit,其中 DSL 是接收者类型,而 init 是一个 lambda 表达式,它可以直接访问 DSL 类的成员。
🔹构建层次结构
你可以使用 lambda 表达式来构建一个嵌套的结构,这在构建树形结构或配置文件时特别有用。例如,构建 UI 或 HTML 模板时,嵌套的 DSL 会让代码看起来更简洁。
1 | class HtmlBuilder { |
这里,我们创建了一个简单的 HTML 构建器 DSL。div 函数会在 HtmlBuilder 类中创建一个新的 div 标签,并可以嵌套其他标签。你可以在外部 html 块中定义结构,从而形成一个层次化的结构。
🔹操作符重载
在 Kotlin 中,你可以重载操作符,创建自定义的语法。这让你能够使用自然的语法来表达一些操作。
1 | class Vector(val x: Int, val y: Int) { |
在这个例子中,通过重载 plus 操作符,你可以使用 + 来直接操作自定义的类 Vector,使得代码更具有直观性和简洁性。
🔹 Kotlin 中构建 DSL 的常见应用
- HTML DSL:用于生成 HTML 文档,类似于上述的例子,可以通过嵌套的 lambda 构建 HTML 标签。
- UI 构建器:许多 UI 框架(如 Jetpack Compose)使用 DSL 来简化 UI 组件的声明式布局。
- 配置文件:可以为 JSON、YAML 或 XML 等配置格式创建 DSL,使其更加自然易读。
- 构建工具:如 Gradle 就是使用 Kotlin DSL 来定义构建脚本。
🔹示例:Kotlin DSL 构建数据库查询
假设你想要构建一个用于数据库查询的 DSL,使得查询语句更加优雅和简洁。
1 | class QueryBuilder { |
说些什么吧!