Kotlin分享(三)

Kotlin分享(一)

Kotlin分享(二)

Kotlin分享(三)

Kotlin分享(四)

Kotlin分享(五)

Kotlin 协程 coroutines

函数类型

    kotlin中函数不仅仅扮演了执行者的身份,他将参与到数据传递这条反应链之中。实际上在java中,我们也有这种的操作,那就是callback。仔细想想,所有的回调实际上目的都是把函数当做参数传递过去。

    PS: 题外话,我们都知道面向对象编程,再此之前是面向过程编程,那么在此之后呢?未来的趋势是什么并不清楚,但是就现在来说,函数式编程的思想正在快速传播(虽然它由来已久)。个人认为java8的lambda特性的添加就是java在函数式编程中做出的妥协或者说顺应时代潮流。当然c++还是老顽固的样子,并不是很支持函数式编程,不过并不是说c++不能使用函数式编程,至少函数指针还是有的。

    回到主题上,kotlin中有一种特殊的类型,叫做函数类型,比如我们可以定义一个函数类型的变量

var listener:((a:Int)->Int)? = null

    提取其中的类型    ((a:Int)->Int)?  其中?的作用是表示能够为空,实际上也是修饰符,再拨出来,所以真是的类型是  (a:Int)->Int 和java的lambda很像对不对, ->前面的部分表示参数, ->后面的部分表示返回值。所以listener就表示一个 输入一个int类型参数,返回一个int型值的函数。

var listener:((a:Int)->Int)? = null

var callback:((a:Int,b:Int)->Int)? = null

val plus = {x: Int ,y: Int -> x+y}

fun lambdaTest(){
    listener = ::sum
    
    listener = temp@{return@temp it+1}
    
    listener = {it+1}
    
    callback = {a, b ->  a+b}

    callback = {_, b ->  b}
}

fun sum(x:Int):Int{
    return x+1
}

    比如上面,看listener的赋值方式,我们找到了三个赋值的方法。使用一个现有方法,加上:: 就能将该方法当做值赋值给变量。

    第二种和第三种本质当都是匿名方法,区别在于temp@ 为什么要添加这个呢? 如果这个方法体不是一行能解决的,那就一定要这个了,只有表达式等于返回内容的时候可以省略return。

    还有一个就是 it 这个东西,如果这个函数类型只有一个输入值,那么我们可以省略输入变量的定义,直接使用it来代表这个参数,如果是过个参数,那么就要列出参数名了,比如像callback这样的。

    再来看 _ 符号,这个符号表示,我不关心这个参数,我在方法中不会使用它,所以使用_来表示参数名。

    然后是plus函数变量的定义,我们省略了类型,所以这个时候就需要在定义的时候显示指定x,y的类型(如果没有省略变量类型,那么定义的时候就能省略参数类型)。

函数类型参数

    既然有了函数类型的变量,那么理所当然也有函数类型的参数,实际上函数类型的参数在实际使用中会更多一点。

fun <T> lock(lock: Lock, body: () -> T): T {
    lock.lock()
    try {
        return body()
    }
    finally {
        lock.unlock()
    }
}

fun toBeSynchronized() = sharedResource.operation()

val result = lock(lock, ::toBeSynchronized)


val result = lock(lock, { sharedResource.operation() })

    基本的使用方法和函数类型变量没有区别。

    不过有一个有趣的地方在于我们可以这样

lock (lock) {
    sharedResource.operation()
}

    当函数类型参数是最后一个变量的时候,我们可以将他提到括号外面!如果这个时候没有其他参数,我们甚至可以省略括号。

 

lambda表达式

    好了,我们要介绍lambda表达式了!

    如上

    好了,lambda表达式介绍完了。

    ???????什么情况。没错,你已经学会使用lambda表达式了, 那个带  -> 的奇怪东西就是lambda表达式,而且你已经看到了他的用法。

 

匿名表达式

    上面的lambda表达式,比如  {a,b-> a+b } 这个么表达式,我们没有指定它的返回值。这是lambda通过推断自己填上的返回类型。如果我们不希望这样,那么就需要使用匿名表达式了

fun highFun(listener:()->Int){

}

highFun(fun():Int {
        var a = 1
        return a
    })

highFun(fun():Int = 1)
highFun(fun() = 1)

     和普通函数一样啊,只是不用名字了而已。这种匿名函数也能够当做参数直接传递给函数变量参数。

    这里有个有趣的地方,就是lambda表达式和匿名函数的区别。区别在于return,只需要记住一个点,return就是寻找最近的fun关键字,退出上一层fun。比如匿名函数有fun,所以return 直接退出匿名函数,但是lambda没有fun的,所以想要退出lambda就需要使用@符号。

一种奇特的类型

    我们之前有介绍过额外方法

fun Int.test(other:Int):Int{
    return this + other
}

     比如这样,我们在int类型上添加一个额外方法。

    现在,我们可以将这个额外方法当做一个类型:就问你怕不怕    

var test2:Int.(other:Int)->Int = {other ->  this+other}

    比如这样,我们定义可一个test2的变量,类型是 Int.(other:Int)->Int 相比于普通的函数类型多了一个Int.的限定符。这就表示,这个方法是用在Int上的,给int添加一个额外方法。

    我们可以像使用test一样使用test2

    1.test(2)

    1.test2(2)

    他们的效果是一样的。

    关于test2,我们还使用了lambda表达式来给这个东西赋值。参考上面的匿名方法,我们也可以使用匿名方法来赋值

var test3:Int.(other:Int)->Int = (fun Int.(other:Int):Int = this + other)

    效果是一样的。

    相比于额外方法,它是一个类型,既然是类型,就能当做参数,比如

fun lambdaTest2(aa:Int.(other:Int)->Int){
    1.aa(2)
}

lambdaTest2(test2)

    更坑爹的事情在于 当一个参数是 (Int,Int)->Int类型的时候,我们可以使用 Int.(Int)->Int来凑数

fun lambdaTest3(aa:(Int,Int)->Int){
    aa(1,2)
}

lambdaTest3(test2)

    

内联函数 inline

    在java中,我们可能对于inline并不重视,用的也不多,但是在kotlin中,inline的作用就大大增加了。为什么?对于所有的高阶函数(拥有函数参数的方法),在编译运行的时候其实会比较耗时,kotlin会将函数参数自动生成一个类,然后在运行的时候还需要创建一个类,并且调用类的方法。我们知道这是耗时的操作。

fun inLine(sum:(Int)->Int){
    Log.d("sss","haha"+sum(1))
}


inline fun inLine2(sum:(Int)->Int){
    Log.d("sss","haha"+sum(1))
}

fun inLinePlus(base:Int):Int = base+1

inline fun inLinePlus2(base:Int):Int = base+1

fun inLineTest(){
    inLine(::inLinePlus)
    inLine { it+1 }

    inLine2(::inLinePlus)
    inLine2 { it+1 }
} 

//编译后
public static final void inLineTest() {
        inLine(InlineTestKt$inLineTest$1.INSTANCE);
        inLine(InlineTestKt$inLineTest$2.INSTANCE);
        Log.d("sss", "haha" + inLinePlus(1));
        Log.d("sss", "haha" + 2);
    }

    PS:以上是编译前后的代码

    所以和java一样,inline就应运而生了。inline的性质倒是没有区别,直接使用代码替换函数调用的地方。

noinline

    贱人就是矫情……有些时候我们可能需要让inline函数中某个函数参数非内联

inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {
    // ...
}

    那么就在这个参数上面添加noinline的关键字……

 

non-local return

    前面有提到过,对于普通的高阶函数,在函数参数中直接调用return时错误的,需要添加lable,但是对于inline函数,我们可以直接调用return 

fun inLine(sum:(Int)->Int){
    Log.d("sss","haha"+sum(1))
}


inline fun inLine2(sum:(Int)->Int){
    Log.d("sss","haha"+sum(1))
}

fun inLineTest(){
    
    inLine { return } //错误
    
    inLine2 { return } //正确,return 会退出inLineTest
}

   

    在inline中有一种情况,我传入的函数参数包裹在其他方法调用中,那么就需要添加crossinline标记,防止body方法中的return作用歧义。

inline fun f(crossinline body: () -> Unit) {
    val f = object: Runnable {
        override fun run() = body()
    }
    // ...
}

    PS:只有在inline方法中会出现错误,如果你不知道什么时候需要添加crossinline不要着急,编译器会提示错误,并且提示你添加关键字的

reified type parameters

open class Base2{

}

class TreeNode:Base2(){
    var parent:TreeNode? = null
}

fun <T> TreeNode.findParentOfType(clazz: Class<T>): T? {
    var p = parent
    while (p != null && !clazz.isInstance(p)) {
        p = p.parent
    }
    @Suppress("UNCHECKED_CAST")
    return p as T?
}

fun inLineTest(){
    var treeNode:TreeNode = TreeNode()

    treeNode.findParentOfType(Base2::class.java)
}

    定义一个TreeNode类,用来保存树的节点,然后定义一个findParentOfType方法,用以搜索TreeNode对象的父节点直到是类型T为止。

    为什么我们需要传入Base2的class类型进去呢?因为我们无法使用   p is T 这样的类型判断。

    不过对于Inline方法,我们还是有补救方法的,在T前加上前缀reified

inline fun <reified T> TreeNode.findParentOfType(): T? {
    var p = parent
    while (p != null && !(p is T)) {
        p = p.parent
    }
    @Suppress("UNCHECKED_CAST")
    return p as T?
}

treeNode.findParentOfType<Base2>()

    这样就不需要传入class参数,能够使用is来判断。

    甚至,我们还可以这样用

        inline fun <reified T> membersOf() = T::class.members

    T就真的像一个类型一样了,而不仅仅是一个通配符。

inline属性

    我们知道kotlin中的属性访问默认使用get 和set ,但这也是方法调用,会消耗开销,所以kotlin也提供了内联属性

val foo: Foo
    inline get() = Foo()

var bar: Bar
    get() = ...
    inline set(v) { ... }

inline var bar: Bar
    get() = ...
    set(v) { ... }

 

转载于:https://my.oschina.net/zzxzzg/blog/1590622

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值