kotlin知识要点梳理(未完)

前言:大部分人可能写了好几年kotlin,各种协程,高阶函数都有使用过,顶层函数也涉及到过,遇到不会的找找资料,参考别人的写法,完成功能。最近抽时间针对kotlin学习了一下,对kotllin的理解又深入了一些。\color{red}{重点是怎么看懂同事写的代码}


个人认为真正需要开发者关注的主要还是下面几个点:
1.高阶函数
2.扩展函数
3.kotlin泛型
4.协程相关


1.高阶函数

一句话概括:函数中有lambda就是属于高阶函数, 函数的函数就是高阶函数.

下面举几个常见的例子:

// 高阶函数定义
fun test(number: Int, lambda : (Int) -> String) : String = lambda.invoke(number)

//函数调用
 var r : String = test(777) { /*it: Int ->*/
        "数据是:$it"
    }

上面是最简单的一个高阶函数, lambda表达式最后一行是返回值为String类型。
需要特别说明的是高阶函数的右边是函数的声明,实现是调用时候的{ } 中的内容 调用时候,会执行lambd表达式 因为只有一个参数,直接可以用it代替。

定义test方法,返回一个匿名方法,传入两个int值,返回一个字符串,注意调用时候是带两个括号
    val test = fun()
            : (Int, Int) -> String
            = {n1, n2 -> "两个数相加:${n1 + n2}" } 

//  调用方法
test()(1000, 1000)

用在登录场景: 可以看下注释内容 ,常规我们可能要写一个接口定义成功失败方法传进去 回调。用下面高阶函数在这种场景其实可以不用定义。只是举例说明场景,实际项目看具体情况使用

/**
 * 高阶函数定义,传入名字,密码,回调函数 这里的回调函数是声明:声明一个回调传入String,不需要返回值。实际项目string可以是某个对象
 */
/高阶函数定义,传入名字,密码,回调函数 这里的回调函数是声明:声明一个回调传入String,不需要返回值。实际项目string可以是某个对象/
private fun loginEngine(userName: String, userPwd: String, responseResult: (String) -> Unit) {
    // mock网络请求
    if (userName == "詹姆斯" && userPwd == "123456") {
        responseResult("{\"code\"\"200\", \"data\":\"XXXXX\"}")
    } else {
        responseResult("{{\"code\"\"404\"}")
    }
}

// 调用
  loginEngine("詹姆斯", "123456") { 
        if (it.contains("200")) println("最终登录的结果是:登录成功") else println("最终登录的结果是:登录失败!!")
    }
2.高阶函数+扩展函数

写过一段时间kt的应该都用过let、apply、run、这几个API,天天用,很多人一直都不懂原理。网上也有专门讲这几个扩展函数的:https://mp.weixin.qq.com/s/kqUA_t2C_cT-5lVtuBdU0Q 可以看看。分析下run方法源码如下:

public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}
 
//let源码
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

1.方法使用了inline修饰,内联就是会把代码拷贝到调用地方,不用单独多创建一个对象出来
2. 入参T 使用泛型,R代表返回值,注意看T.run 代表给任意类型扩展一个run方法
3.lambda的输入声明的是T.() -> R 代表输入是T的匿名函数 输出是R本身,最后返回lambda表达式
 

第二点提到使用T.() 就说明调用的地方 { } 里面是this。 如果是(T)- > R 在调用的 地方{ }里面就是it 如上图。AS工具里面也可以看到如下:


image.png

参照源码,自定义一个如下:

fun <T> T.自定义运行方法(block: T.(Float) -> Boolean) {
    // this == T本身 == 调用者本身 
    block(11111f) // 调用Lambda
}

//调用:
    "测试".自定义run {
        println("谁调用 $this 传入的价格是:$it ")
        true }

为了更好理解,我直接命名“自定义运行方法”为方法名字,也就是对任意类型,扩展一个“自定义运行方法”。方法声明为一个lambda表达式,在调用时候 { } 里面入参是一个浮点型,it可以获取到,返回是boolean类型。看下面图黄笔划线地方。this代表谁调用的,it是lambda表达式的输入 里面我直接写死了1111f.

image.png

扩展函数

fun <T> T.abc() {
    // this == T本身 == 调用者本身 
    println("abc我是:$this")
}

比如上面代码,给T扩展一个函数,后面使用任意类型就可以.abc方法打印本身。也就是说 我们可以为OKHttp、View、Activity等很多方法扩展一些方法,而不改原始的类,比如定义如下方法,就可以String.toast()了:

fun Any.toast() {
    // Any == this
    println("任意类型调用,你的值是:$this")
}

 

3.kotlin泛型

直接上问题,在java中:

List<Object> aa   = new ArrayList();
List<String  > bb  = new ArrayList();

请问  aa = bb 是否可以

\color{Blue}{虽然String是Object的派生类,但是装到list里面,是不行的, 因为aa.add(new Data()), 可以存一些别的东西进去,但是get 取的时候不是String有问题 如果强转就有问题。}
结论就是List<父> aa = new ArrayList<子>(); 只能获取,不能往里面加,因为加一些其它类型就有问题。下面直接上图更清晰:

list.add(ziClass);.jpg

那看下另外 ?super 场景呢

的孩子,但是可以set 表明是谁的娃.jpg

\color{red}{******结论******}
? extends XX == out \color{red}{ out}代表只能往外取,程序员视角是只能\color{red}{获取}
? super XX == in 不知道爹是谁,只能\color{red}{in} 进去改 程序员视角是只能\color{red}{输入}

4.协程

定义
由kotlin官方提供的一套线程框架API , 借助KT语言优势,用看起来同步方式写出异步代码, (同一个代码块可多次切换线程)

优点
可以轻松实现两个并行请求,比如获取用户ID 再登陆,之前可能是嵌套。要等第一个请求成功后再发起第二个请求。执行效率慢一倍,现在用协程直接可以并行使用async 实现

launch函数
意思是创建一个新的协程运行在指定的线程上。

suspend解释:
suspend 其实是一个提醒,是函数创建者对调用者提醒,告诉要在携程里面调用 ,真正挂起是靠携程里面实际代码 比如withContext切线程
协程中suspend只是一个标记 ,挂起,挂起的对象是协程 ,从当前正在执行的线程挂起,就是会脱离当前线程。比如在主线程运行 执行到一个挂起函数,挂起函数用withContext切换到IO线程执行,执行完后会自动切换回原来的主线程继续执行,切回来的动作叫做 resume。

suspend 编译后如下图, continuation 其实就是callback 单词意识就是持续继续 保证后面剩余代码恢复工作


Pasted Graphic 16.jpg

什么又是非阻塞试挂起
不卡线程 就叫非阻塞式挂起, 网上说的 协程是非阻塞 线程是阻塞有个前提条件,单线程场景下 。 耗时操作 分两种:1种是IO操作一种CPU计算 而网络就属于IO,性能瓶颈是IO,和网络交互 所以线程被网络交互阻塞 但阻塞没办法避免。 协程没有比线程更高级,只是自动切回来/协程的 非阻塞式挂起只是用阻塞的方式写出非阻塞的代码而已。并没有更高效的 就是上层框架。携程的本质还是线程。包括官网的文档,也有误导,对比Thread sleep.

状态机模式
里面有个枚举。挂起 没挂起,resume, 主要实现是ContinuationImpl类,大概会初始化变量,把主线程的变量copy出来, 主要为了恢复 实力滑 invokeSuspend 函数 里面一个循环,判断状态机 执行请求好事任务,执行完成后

协程关键字

做两个小测试,看下是否能回答得上,下面打印hello word 大部分人都能回答上,在看再下面一个代码估计大部分人都会回答错:

fun main() = runBlocking { // this: CoroutineScope
    launch { // 在 runBlocking 的作用范围内启动新的协程
        delay(1000L)
        println("World!")
    }
    println("Hello,")
}

下面代码 1、2、3、4打印顺序如何:

fun main() = runBlocking { // this: CoroutineScope
    launch {
        delay(200L)
        println("Task from runBlocking 1")
    }
    coroutineScope { // 创建新的协程作用范围
        launch {
            delay(500L)
            println("Task from nested launch 2")
        }
        delay(100L)
        println("Task from coroutine scope  3") // 在嵌套的 launch 之前, 这一行会打印
    }
    println("Coroutine scope is over 4") // 直到嵌套的 launch 运行结束后, 这一行才会打印 }
}

截图如下,是不是回答错了,打印的是3-1-2-4, 因为coroutineScope关键字:\color{Blue}{coroutineScope 构建器来自行声明作用范围. 这个构建器可以创建新的协程作用范围, 并等待在这个范围内启动的所有子协程运行结束,coroutineScope 在等待子协程运行时, 不会阻塞当前线程}

image.png

协程关键字

CoroutineScope 协程的作用域
CoroutineDispatcher 协程的调度器
CoroutineContext 协程上下文

异常捕获的注意事项

1.协程里面的异常捕获,需要在里面单独处理

fun main(args: Array<String>) {
    try {
        GlobalScope.launch {
            println(test())
        }
    } catch (e:Exception) {

    }
    Thread.sleep(100)
}

suspend fun test(): String = withContext(Dispatchers.Default) {
    throw RuntimeException("this is exception")
    "测试..."
}

个人理解,suspend函数会从当前协程线程上切走,也就是test()运行在协程的子协程上,但是捕获异常还是捕获的当前协程,所以捕获不到。 使用如下方式捕获:

 GlobalScope.launch {
        try {
            println(doSomething())
        } catch (e:Exception) {
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,137评论 6 511
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,824评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,465评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,131评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,140评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,895评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,535评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,435评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,952评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,081评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,210评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,896评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,552评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,089评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,198评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,531评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,209评论 2 357

推荐阅读更多精彩内容