Kotlin 空安全机制会限制你在定义变量时不及时赋值,但若此时你还无法及时赋值,可使用 lateinit
关键字修饰,编译器将暂时放弃校验它,但之后的空判断都需要自己检测了
lateinit var someStr : String
当然了,你同样可以通过先赋初始值,之后更新的方式
var someIndex : Int = 0
若有些变量不可避免的会出现赋值为 null 的情况是,可通过 ?
字符设置此变量可为 null
var someStr : String? = null
var someIndex : Int ? = null
使用 val
关键字同样可以定义变量,但它代表只读变量
val className = "SOME_CLASS_NAME"
可以看到我并没有写 : String
但不会报错,这是因为 Kotlin 会自动做类型推断
定义常量可通过 const
关键字,且只能修饰 val
类型
const val className = "SOME_CLASS_NAME"
companion object
代码块中可定义静态变量、常量
companion object {
val className = "SOME_CLASS_NAME"
var classId = -1
}
使用 fun
关键字可声明方法,若此方法参数不可为 null 且无返回值:
// Unit 可省略
fun initView(context : Context) : Unit{
// do something
}
fun initView(context : Context) {
// do something
}
若此方法参数可为 null 且有返回值,则需在参数后加 ?
,并在方法体前加入返回值类型,如 String
:
fun initView(context : Context?) : String{
// do something
return ""
}
使用 constructor
关键字即可声明构造函数
var someStr : String = ""
constructor(someStr:String){
this.someStr = someStr
}
并可通过 init
关键字声明初始化代码块
var someStr : String? = null
constructor(someStr:String){
this.someStr = someStr
}
init {
// do something
}
同时还有另一种方法可更方便的实现类初始化,那就是在 class xxx
后直接设置 constructor()
class KotlinTest constructor(someStr : String?, someIndex : Int){
}
将 变量、常量、方法等直接写至 package
下而非 class
内,这样调用时即可
package xxx.xxx.xxx.xxx
val newTimeData = "sometime"
fun getNewTime() = "do something"
object SomeUtils{
// do something
}
// 调用
var teseData01 = newTimeData
var teseData02 = getNewTime()
比 companion object
更方便,类名都不用写,鼓掌!
如果留意 getNewTime()
你会发现他只用了一行就完成了所有工作,这是因为 Kotlin 支持这种代码简化的方式:如果你的代码块只有一行,那么可以这样省略
若使用 object
关键字修饰某个类的话,则表示它是一个单例类
object SomeUtils{
val className = "SomeUtils"
public fun getName() = this.className
}
在其他类中可直接这样调用其方法
var name = SomeUtils.getName()
使用 object
关键字修饰类时,此类中的方法全部为静态方法
一套由 Kotlin 官方提供的线程 API,更加方便的线程框架。suspend
可实现非阻塞式挂起,使得并发任务的操作难度大大降低,且能通过 whitContext
消除回调嵌套,解决回调地狱问题。最有特点的是:能够在同一个代码块实现进行多次线程切换
添加依赖地址
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1"
GlobalScope.launch(Dispatchers.Main) {
val userInfo = getUserInfo() // 请求数据
userInfoTv.text = userInfo // UI 更新
}
多接口并行请求完成后合并数据
GlobalScope.launch(Dispatchers.Main) {
val userInfo = getUserInfo() // 请求数据
val logo = getLogo() // 请求数据
val user = userDataUpdate(userInfo, logo) // 合并
show(user) // 显示
}
whitContext
消除回调嵌套
GlobalScope.launch(Dispatchers.Main) {
val logo = withContext(Dispatchers.IO){
getLogo() // 请求数据
}
val name = withContext(Dispatchers.IO){
getName() // 请求数据
}
// ···
}
也可这样写,跟上边的方式结果相同
GlobalScope.launch(Dispatchers.Main) {
val logo = spendingGetLogo()
logoIv.setImageBitmap(logo)
}
suspend fun spendingGetLogo(){
withContext(Dispatchers.IO){
getLogo() // 请求数据
}
}
挂起后协程暂时从线程脱离,若此线程非主线程之后线程会被回收或再利用,若此线程是主线程那么主线程继续执行之后的代码。但方法会切换至新指定线程中继续执行,且当方法执行完毕后,线程会自动切会原线程,所以挂起的本质就是切线程处理,完成后自动切回原线程
为什么挂起函数只能在协程,或另一个协程里调用?
挂起后需要恢复,也就是切回原线程,恢复的功能是属于协程的,所以被挂起的函数如果不是在协程中调用,也就无法实现恢复
怎么挂起?
withContext(Dispatchers.xxx)
内部包含有自带的挂起函数,
suspend
的作用?
函数的 创建者
对函数的调用者
的提醒,表示这是一个耗时函数,所以创建者用挂起的方式放在后台运行,所以要调用我的话得在协程里。
怎么自定义 suspend
函数?
当此函数比较耗时,就把它定义为挂起函数,如I/O、计算或等待(网络通信也属IO的一种)
suspend fun xxx(){
withContext(Dispatchers.xxx){
// do something
}
}
非阻塞式表示当前线程不卡顿,协程可以用看似阻塞的代码做到非阻塞的处理操作
Java中线程切换也是非阻塞式的,但它跟协程挂起的区别在于:单线程的耗时操作会产生阻塞,而协程可以利用挂起函数将耗时操作切换至另一个线程继续处理
不过从协程的写法上来看,两行代码连续写的方式好像是同一线程在处理的,但实际上已经被切换线程继续处理,只不过处理完成后又自动切回来原线程,而这个过程无法肉眼感知
若您有遇到其它相关问题,非常欢迎在评论中留言,我和其他读者小伙伴们将帮助解决并持续更新至此文,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!推荐 朱凯老师(扔物线)的 Kotlin 学习网站:https://kaixue.io,棒!