Kotlin高级
引言
本节主要描述Kotlin的高阶函数,以及also,run,let,apply等写法和其原理
声明高阶函数
声明高阶函数就像声明一个变量一样,没有具体的实现,当然它也可以被作为另一个函数的形参,传递给其他函数使用
var m1:() -> Unit // 定义了一个无返回值。无参数的函数
var m2:(Int, Int) ->Int
// ..
定义高阶函数
var m1: (Int, Int) -> Int = {number1, number2 -> number1 + number2}
var m2: (String, String) -> Unit = {str1, str2 -> println("s1:$str1, s2:$str2")}
var m3: (Int, Int) -> Int = {i1, i2 ->
i1 + i2 // 不需要return
}
var m4: = {number1:Int, number2:Int -> number1 + nubmer2}
var m5 = {i1:Int,i2:Int -> i1 + i2}
高阶函数作为参数
fun login(username:String, password:String,check:(String,String)->Boolean) : Boolean{
if (check(username,password)) {
return LoginMgr.login(username,password)
} else {
return false
}
}
// 调用
fun main() {
login("123","123") { username, password->
TextUtils.isEmpty(username) && TextUtils.isEmpty(password) // 这里不需要return
}
}
高阶函数
let高阶函数
let可以拿到{}中的返回值
fun main() {
var response: String? = "success"
var res1 = response?.myLet{ it == "success" }
}
fun <T, R> T.myLet(m:(T) -> R): R = m(this)
给T传递一个myLet扩展函数,扩展函数使用了一个高阶函数,这就可以resonpse.myLet{}了,因为T是任意类型的,因此任意函数都可以调用.myLet()函数
为什么不是response.myLet()呢?原因myLet中的参数为高阶函数,因此可以直接response.myLet{}
在kotlin提供的let函数中,我们可以直接使用it来代替response的,因此,这里应该增加一个参数T,传递给m:(T)->R,从而有response.myLet{ it-> // .. }
apply高阶函数
apply依然调用者,如T.apply就返回T
response.myApply{ println(this.length) } // 这里可以直接使用this来代替response
fun <T> T.myApply(m:T.() -> Unit): T {
m()
return this
}
给他T传递一个myApply的扩展函数,注意,与myLet的区别是参数m也增加了T.(),这就意味着m也成了T的扩展,因此在m中可以使用T的成员变量,和T即this。
而在response.myApply{}中也可以直接使用this
Also高阶函数
response.myAlso{ println(it.length) } // 和apply的区别是,在高阶中不能使用this,也不能直接使用response的成员属性了
fun <T> T.myAlso(m:(T)->Unit):T{
m(T)
return this
}
和apply对比可知,myAlso中m:(T),传递了T参数,即如果是response调用,则在myAlso{}中可以直接使用it代替response
run高阶函数
run和let函数很类似,但在{}中不能使用T,如response.myRun{}的{}中不能使用it来代替response,也可能返回{}中返回的内容
var res = response.myRun{ true } // res == true
fun <T, R> T.myRun(m:()->R): R = m()
with高阶函数
这个with类似于js中的with
var res = myWith(response) { // 返回true
length > 0 // 可以直接使用response的成员属性
}
fun <T, R> T.myWith(receiver:T, m:T.()->R): R = receiver.m()
repeat高阶函数
repeat类似一个循环,0-n的一个循环,如repeat(n),循环n次
repeat(5) {
println(it)
}
fun repeat(times:Int, m:(Int)->Unit):Unit {
for (index 0 until times) {
m(index)
}
}
isTrueThen高阶函数
response.isTrueThen{
setStatus(it)
}
fun <R> Boolean.isTrueThen(m:(Boolean)):R? {
if (this) {
return m(this)
} else {
return null
}
}
notEmptyThen高阶函数
response.notEmptyThen{
doHandler(it)
}
fun <T, R> T.notEmptyThen(m:(T)->R): R? {
if (this != null) {
return m(this)
} else {
return null
}
}
泛型读取权限
如果C extends A,则并不能使用List<A> list = new ArrayList<B>(),而只能使用List<? extends A> = new ArrayList<B>(); 或者 List<? super B> = new ArrayList<B>();
在java中,? extends A,表示只能读取,不能修改,而? super A表示只能修改不能读取
在Kotlin中,通过 out A,表示只能读取,不能修改,而in A表示只能修改不能读取
为什么有? extends A只能读,? super A只能写,试想如果相反,由于继承关系,子类的范围大于父类。那么写入的都是范围更大的类,读出来是更小的类,这就使得当写入两个节点是兄弟节点时,读出来无法强转了。
因此? extends A只能写,? super A只能读,这样就能保证写入的类不会是范围更大的类,读出来之后,可以直接函数调用
协程
Kotlin协程在Andriod中可以优雅使用,并可以减少
fun displayMethodOk() = runBlocking { // main环境
launch { // main环境
val def = async(Dispatchers.IO) { // 异步环境
val result = okHttpClient.newCall(mRequest).execute().body()?.string()
}
// main环境
textView.text = def.await()
}
}
注意,上述使用的是组赛式的协程,这就意味着在运行时,不能进行其他操作,要等待其任务完成后才能继续下一个任务。也就是非要等launch里面的任务都执行完,才能进行下一个任务。
或者
fun displayMethodOk() {
GloableScope.lauch(Dispatchers.Main) {
val pro = ProgressDialog(this)
pro.setMessage("正在执行...")
pro.show()
// main
withContext(Dispatchers.IO) { // 异步
//...
}
pro.hide()
}
}
上述就是使用非组赛式,但是它像守护线程一样,当主线程挂了后,它就挂了,所以在需要增加如下写法:
fun displayMethodOk() {
var job = GloableScope.lauch(Dispatchers.Main) {
// ...
}
job.cancelJoin() // 或者直接使用 job.join()
}
总结
本节主要描述了高阶函数,泛型协变,以及协程的用法