一、进阶用法
解构: 解构指的是将对象的多个属性分别赋值给多个变量的过程 fun main(args: Array
operator:将⼀个函数标记为重载⼀个操作符或者实现⼀个约定
fun main(args: Array
// 左闭右闭区间 for (i in 1..10) { println(i) } // 左闭右开区间 for (i in 1 until 10) { println(i) } // 倒序 for (i in 10 downTo 1) { println(i) } for (i in 1..10 step 2) { println(i) } repeat(10) { println(it) } val list = arrayListOf
集合操作符
val list = arrayListOf
常用集合操作符
元素操作类 contains —— 判断是否有指定元素 elementAt —— 返回对应的元素,越界会抛IndexOutOfBoundsException firstOrNull —— 返回符合条件的第⼀个元素,没有 返回null lastOrNull —— 返回符合条件的最后⼀个元素,没有 返回null indexOf —— 返回指定元素的下标,没有 返回-1 singleOrNull —— 返回符合条件的单个元素,如有没有符合或超过⼀个,返回null 判断类 any —— 判断集合中 是否有满⾜条件 的元素 all —— 判断集合中的元素 是否都满⾜条件 none —— 判断集合中是否 都不满⾜条件,是则返回true count —— 查询集合中 满⾜条件 的 元素个数 reduce —— 从 第⼀项到最后⼀项进⾏累计 过滤类 filter —— 过滤 掉所有 满⾜条件 的元素 filterNot —— 过滤所有不满⾜条件的元素 filterNotNull —— 过滤NULL take —— 返回前 n 个元素 转换类 map —— 转换成另⼀个集合 mapIndexed —— 除了转换成另⼀个集合,还可以拿到Index(下标) mapNotNull —— 执⾏转换前过滤掉 为 NULL 的元素 flatMap —— ⾃定义逻辑合并两个集合 groupBy —— 按照某个条件分组,返回Map 排序类 reversed —— 反序 sorted —— 升序 sortedBy —— ⾃定义排序 sortedDescending —— 降序
作用域函数
run {...} with(T) {...} let {...} apply {...} also {...} takeIf{...} takeUnless{...} repeat(number: Int){...} let和run的区别: 都会返回闭包参数,区别let有闭包参数,run没有闭包参数 also和apply的区别: 都不会返回闭包结果,区别also有闭包参数,apply没有闭包参数 // 常用于设置同一个控件的多个属性 with(user) { this: User this.name = "with" } user.apply{ this.name = "with" }
操作符实现原理
public inline fun
运算符重载与中缀表达式
运算符重载
fun main(args: Array
Expression |
Translated to |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
中缀表达式
// 运算符有上限,需要使用中缀表达式来扩展 infix fun Int.vs(num: Int): CompareResult = if (this - num > 0) { CompareResult.MORE }else if (this - num > 0) { CompareResult.LESS }else { CompareResult.EQUAL } fun main(args: Array
反引号
反引号:键盘左上角与波浪线在一起的符号 在 Kotlin 中, 可以用反引号解决关键字冲突问题 将一个不合法的字符变成合法的 fun `1234`() {} fun ` `() {} fun ` `(){} 应用场景: Kotlin 有个特有的访问修饰符 internal,它允许当前模块的类只能在当前模块使用而不能被其他模块访问, 但是 Kotlin 与 Java 是完全兼容的,所以 Kotlin 支持了这种特性的话 Java 也必须要支持,可是 Java 是没有 internal 这样的关键字的,所以当 Kotlin 声明为 internal 时 Java 会把它声明成 public, 这样的话在 Java 中跨模块是能够直接访问到的。所以在这种场景下,如果你确定你的类是不希望被 Java 访问到的话,就可以在类名上做一些特殊不合法的字符,这样的话就可以只在 Kotlin 中访问而不能被 Java 访问。
比较对象
Kotlin Java a == b a.equals(b) a === b a == b public static void main(String[] s) { String string = "string"; String newString = new String("string"); System.out.println(string == newString); System.out.println(string.equals(newString)); }
类型链接
typealias 类型别名 = 已有类型 package kotlin.collections @kotlin.SinceKotlin public typealias ArrayList
变量、常量与只读
var
空安全是如何实现的
空指针: 尝试调用空对象的成员变量或方法会触发空指针异常 空安全机制: 每次引用对象的时候,都去进行对象判空,在运行期避免对象空指针 通过静态代码检查,编译插件检查,在编译期避免空指针异常
可空类型
Kotlin代码 fun main(args: Array
非空类型
Kotlin代码 fun main(args: Array
内联的特殊情况
在Kotlin中,内部Lambda是不允许中断外部函数执行的 inline 的 Lambda 可以中断外部函数调用 inline fun test(l:()-> Unit){ l.invoke() return } fun main(args Array
Kotlin的真泛型与实现方法
支持限定泛型参数类型
// 如下代码表示T 需要直接或间接继承或实现 Callback ,Runnable 类型(是其子类),t 才能被传递进来,否则编译器报错 class Test
kotlin的泛型是真范型
kotlin 的泛型是真泛型,不会像java 泛型,在编译后参数都被抹去,直接变成object //以gson库代码举例 java code 如下 public
什么是DSL
DSL 全称是 Domain Specific Language,即领域特定语言。顾名思义 DSL 是用来专门解决某一特定问题的语言,比如我们常见的 SQL 或者正则表达式等,DSL 没有通用编程语言(Java、Kotlin等)那么万能,但是在特定问题的解决上更高效。 创作一套全新新语言的成本很高,所以很多时候我们可以基于已有的通用编程语言打造自己的 DSL,比如日常开发中我们将常见到 gradle 脚本 ,其本质就是来自 Groovy 的一套 DSL:
android { compileSdkVersion 28 defaultConfig { applicationId "com.my.app" minSdkVersion 24 targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } } build.gradle 中我们可以用大括号表现层级结构,使用键值对的形式设置参数,没有多余的程序符号,非常直观。如果将其还原成标准的 Groovy 语法则变成下面这样,是下面这样,在可读性上的好坏立判: Android(30, DefaultConfig("com.my.app", 24, 30, 1, "1.0", "android.support.test.runner.AndroidJUnitRunner" ) ), BuildTypes( Release(false, getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' ) )
kotlin的DSL及其优势
Kotlin 是 Android 的主要编程语言,因此我们可以在 Android 开发中发挥其 DSL 优势,提升特定场景下的开发效率。例如 Compose 的 UI 代码就是一个很好的示范,它借助 DSL 让 Kotlin 代码具有了不输于 XML 的表现力,同时还兼顾了类型安全,提升了 UI 开发效率。 普通的 Android View 也可以使用 DSL 进行描述。下面是一个简单的 UI 布局,左边是其对应的 XML 代码,右边是我们为其设计的 Kotlin DSL 代码
xml
Compose
通过对比可以看到 Kotin DSL 有诸多好处: 1.有着近似 XML 的结构化表现力 2.较少的字符串,更多的强类型,更安全 3.linearLayoutParams 这样的对象可以多次复用 4.可以在定义布局的同时实现 onClick 等 5.如果需要,还可以嵌入 if ,for 这样的控制语句
不使用DSL LinearLayout(context).apply { addView(ImageView(context).apply { image = context.getDrawable(R.drawable.avatar) }, LinearLayout.LayoutParams(context, null).apply {...}) addView(LinearLayout(context).apply { ... }, LinearLayout.LayoutParams(context,null).apply {...}) addView(Button(context).apply { setOnClickListener { ... } }, LinearLayout.LayoutParams(0,0).apply {...}) }
Kotlin 如何实现DSL
1.使用带有尾 lambda 的高阶函数实现大括号的层级调用
2.为 lambda 添加 Receiver,通过 this 传递上下文
3.通过扩展函数优化代码风格,DSL 中避免出现命令式的语义
4.使用 infix 减少点号圆括号等符号的出现,提高可读性
5.使用 @DslMarker 限制 DSL 作用域,避免出错
6.使用 inline 提升性能,同时使用 @PublishedApi 避免不必要的代码暴露