记录些容易遗忘或易混淆的语法结构和关键字
关键字
val
只读的变量
var
可读可写的变量
const val
编译时常量,只能修饰基本数据类型,只能在函数外定义,因为是编译时初始化的。
vararg
函数的变长参数
$
用于字符串模板中变量名或变量值
is
类型检测
in
区间判断
inner
修饰内部类,内部类可以用this@Class
来获取外部类对象,外部类通过对象调用内部类方法,不用inner
修饰的类但写在内部称为嵌套类,外部类通过静态调用获取嵌套类对象,嵌套类不可通过this获取外部类对象
internal
同模块内可见
inline
内联函数修饰符,用于提升运行速度,使得方法压栈的方式变成替换代码直接执行,从而提高效率
sealed
修饰密封类,类似于枚举,结合when条件语句使用,可列举出类的所以情况而不写else,这点比普通的switch要强,便于拓展时不遗漏新的属性
object
对象声明,可方便地实现对象单例
companion object
伴生对象,静态调用
by
委托,代理模式的快速实现,可实现类委托(委托的接口方法实现,无需重复写模板代码),属性委托(委托的get,set方法),可委托给lazy{}
、Delegates.observable
、map
,其中by lazy提供三种现场模式:LazyThreadSafetyMode.SYNCHRONIZED 加入线程锁实现线程安全(默认模式,此模式在单例模式实现中使用方便)、LazyThreadSafetyMode.NONE 无线程安全,占资源少 、LazyThreadSafetyMode.PUBLICATION 无线程安全但取第一个线程计算的值为准。其中by Delegates.observable快速实现对某个对象的观察,当值发生改变时触发回调
operator
识别属性委托的关键字
//属性委托类的写法
class StringDelegate(private var s: String = "Hello") {
operator fun getValue(thisRef: Owner, property: KProperty<*>): String {
return s
}
operator fun setValue(thisRef: Owner, property: KProperty<*>, value: String) {
s = value
}
}
lateinit
修饰变量,代表这个变量使用前一定会赋值,编辑器不必判空
使用于明确在某一位置的初始化情况,同时该属性需是var可变类型;如果可以是任意位置则by lazy更合适
in 0..10
range表达式 代表值在某个范围内,返回类型为boolean
when
表达式 ,类比JAVA的switch,但比switch 功能更加多样
Unit
空类型,类比Java的void
Any
基础类,类比java 的Object
takeIf
判断真假,代替if(true){}语句,比如 a?.takeIf(isPlaying)?.apply{//}
${}
字符串模版,括号中可以写入变量,表达式等,与java不同的是,kotlin中when,if 是表达式,java中if,switch是语句
` `
反引号,作用一:可替代方法名;作用二:使关键字变成普通的字符串
T::class.java
获取泛型class
JvmField
属性注解,消除属性的get,set方法,默认情况下私有属性会有公有的get方法,加上注解后消除get,set方法,默认属性是公有
JvmStatic
注解属性时,属性变成静态,生成静态get,set方法,作用方法时,方法变成静态
单例写法
可以直接使用object对类修饰实现单例,推荐以下写法使用时创建对象,并考虑多线程的创建问题
class SingleC {
companion object {
val sIntance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
SingleC()
}
}
}
匿名函数
顾名思义,匿名函数不显示方法名,但函数的参数必须写在括号内,无参可省略括号
内联函数
函数 | 特点 | 使用 |
---|---|---|
let | it代指,强调作用域,返回最后一行或空,判null方便 | obj?.let{it.todo} |
with | this代指可缺省,返回值为最后一行或return | with(user){todo} |
run | this代指可缺省,返回值同with,判空方便 | obj?.run{todo} |
apply | this代指可缺省,返回对象本身,适用初始化 | obj?.apply{todo} |
also | it代指,返回对象本身,适用链式调用 | obj?.also{todo} |
集合
- 集合排序
val numbers = mutableListOf(1, 2, 3, 4) //可变列表
//随机排列元素
numbers.shuffle()
println(numbers)
numbers.sort()//排序,从小打到
numbers.sortDescending()//从大到小
println(numbers)
//定义一个Person类,有name 和 age 两属性
data class Language(var name: String, var score: Int)
val languageList: MutableList = mutableListOf()
languageList.add(Language(“Java”, 80))
languageList.add(Language(“Kotlin”, 90))
languageList.add(Language(“Dart”, 99))
languageList.add(Language(“C”, 80))
//使用sortBy进行排序,适合单条件排序
languageList.sortBy { it.score }
println(languageList)
//使用sortWith进行排序,适合多条件排序
languageList.sortWith(compareBy(
{ it.score }, { it.name })
)
println(languageList)
注意事项
- 方法入参是常量,不可修改
class Main {
/**
* Kotlin 入参是常量
*/
fun print(a: Int = 1, b: String = "") {
// a = 10; // 错误:Val cannot be reassigned!!!
}
}
- as的判定写法
//正确写法,转换失败自动转换为空对象
var strAble = text as? String
//错误写法2,text不是String时,同样会报异常
var strAble2 = text as String?
- 构建常量类
@file:JvmName("Constant")
放于包声明前面
协程
一个进程有多个线程,一个线程有多个协程,又称之为微线程,kotlin中默认实现了很多协程作用域,即各种各样的scope,可以用写同步代码的方式来书写异步代码
协程库支持
dependencies {
// Kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.4.32"
// 协程核心库
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3"
// 协程Android支持库
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3"
// 协程Java8支持库
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.4.3"
// lifecycle对于协程的扩展封装
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
}
suspend
挂起函数,在协程内部使用,耗时任务会将协程挂起,直到任务完成,与阻塞不同点1.阻塞的进程依旧在内存中,挂起的进程被交换到内存外;2.阻塞一般是等待资源,挂起一般是用户或系统的需要3.阻塞在有资源后激活,挂起在用户主动激活
有个形象的比喻
挂起线程的意思就是你对主动对雇工说:“你睡觉去吧,用着你的时候我主动去叫你,然后接着干活”。
使线程睡眠的意思就是你主动对雇工说:“你睡觉去吧,某时某刻过来报到,然后接着干活”。
线程阻塞的意思就是,你突然发现,你的雇工不知道在什么时候没经过你允许,自己睡觉了,但是你不能怪雇工,因为本来你让雇工扫地,结果扫帚被偷了或被邻居家借去了,你又没让雇工继续干别的活,他就只好睡觉了。至于扫帚回来后,雇工会不会知道,会不会继续干活,你不用担心,雇工一旦发现扫帚回来了,他就会自己去干活的。
launch
创建一个协程,接受三个参数:
- CoroutineContext上下文,代表运行在哪种线程中,有四种模式Dispatchers.Default,
Dispatchers.IO:挂起时运行在默认线程池,挂起恢复后运行在IO线程
Dispatchers.Main:运行在主线程,但主线程后面的代码会先执行,挂起恢复后也在主线程
Dispatchers.Unconfined:挂起恢复后在默认线程池中
也可以自己指定在自定义的线程池中运行; - CoroutineStart启动模式,DEAFAULT,ATOMIC,UNDISPATCHED,LAZY(需手动启动)
- block闭包方法,传入要执行的操作
async
创建个带返回值得协程,返回值是Deferred,继承自Job,新增await方法接受返回值,等待过程中会挂起协程,有结果后恢复协程
launch 和 async 是常用的两种创建协程的方式,一般使用async时是期望有返回值的,在await中获取,由于await是挂起函数,所以await的请求是在协程中,应用情况一般是在一个协程内部需开启另一条协程去获取数据,下面数据继续进行,到await时候等待数据回来
mScope.launch {
// 开启一个IO模式的线程 并返回一个Deferred,Deferred可以用来获取返回值
// 代码执行到此处时会新开一个协程 然后去执行协程体 父协程的代码会接着往下走
val deferred = async(Dispatchers.IO) {
// 模拟耗时
delay(2000)
// 返回一个值
"Quyunshuo"
}
// 等待async执行完成获取返回值 此处并不会阻塞线程 而是挂起 将线程的执行权交出去
// 等到async的协程体执行完毕后 会恢复协程继续往下执行
val date = deferred.await()
}
withContext
不创建新的协程,在指定协程上运行
val job2 = mScope.launch(Dispatchers.IO) {
// 此处是IO线程模式
// 切线程 将协程所处的线程环境切至指定的调度模式Main
withContext(Dispatchers.Main) {
// 现在这里就是Main线程了 可以在此进行UI操作了
}
}
runBlocking
独立使用,delay会阻塞线程,主要用于交接阻塞式与挂起的非阻塞式协同运行的场景
job
构建协程得到的对象,可执行start()–开始,join()–等待执行完毕,cancel()–取消,cancelAndJoin()–等待协程执行完毕再取消
理解协程的运行流程,由于多个协程是运行在一个线程中,正常谁写前面谁先运行,运行完后运行下一个,运行过程中如果被挂起,则让出线程运行下一个,挂起恢复后等正在运行的协程运行完运行该协程
协程类型
GlobalScope
作用于整个应用程序,适用于执行长期运行的异步任务,比如网络请求或者定时任务
MainScope
指定了Dispatchers.Main作为其默认协程调度器,通常用于与UI相关的协程操作,生命周期通常是Activity或Fragment的生命周期
lifecycleScope
生命周期与LifecycleOwner一致,一般是启动activity时执行异步操作使用