Kotlin之心路历程

Kotlin之心路历程

以小白眼光看kotlin前端

kotlin这个小丫环被谷歌扶正为大房两年,期间看过很多博文,不少人也已经把我的项目迁移到kotlin了,固然国外的开发者更给力,直接大部企业开发已经kotlin,也订阅了涛哥的极客时间(一直没时间看,果真看视频太费事,仍是文章能够抽得时间挤一挤学),一直是不想学习啊,一我的的惰性就是这样,java又不是不能用,代码通俗易懂。说句内心话,java8的lambda表达式我都没学,ps:不过好像用java8 lambda表达示的在工做中也没怎么碰到。。java

第一眼看到kotlin,大部分人都是,哇,我被它惊艳了(至少表面上都这么说的),然而本渣狗并无感受(难道就我一个菜狗?),反而以为:啥?为了一个空安全学新的知识,if(x!=null)它不香哦。直到最近换了工做,遗留代码一半是kotlin,糟了,是心动的感受。(不得不上手写了)python

其实最开始上手依然痛苦无比,lambda表达式各类省略,函数是一等公民这句废话几乎每一个博客都写了,可是我看着太抽象啊,我管你几等公民,你告诉我它区别在哪啊,有啥用啊。android

无非下面一些问题

  1. 空安全并无让我香到学习新知识安全

    空安全确实没有香到非学新东西不可,不过,它的设计思路仍是不错的。判空无非两种作法,a. 在编写代码里运行时避免空指针异常,也就是经常使用的if(x!=null);b. 直接静态代码检查,在编译期就避免了空指针,kotlin其实就是这种了,好比定义为String,那么很差意思,别的地方你传个null,编译器就给你整红,非要可空,那后果你本身负,用String?吧。这样的好处是省略了大把逻辑判断,让编译器帮咱们把把关。app

  2. Lambda表达式省略一时爽,不会写的人看不懂异步

    最简单的也是出现最多的,莫过于点击事件了jvm

    //kotlin
    view.setOnClickListener {
    	//TODO
    }
    
    //java
    view.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 
             }
         });
    复制代码

    你们其实看不习惯的缘由就在于,没有()带参数,那我{}里要使用()传递过来的参数怎么办,其实编译器如今已经很智能的告诉咱们了 async

    咱们在{}中能够经过it就能引用这个传递过来的view参数。至于Lambda表达式为何能这么省略,咱们到后面再讲。

  3. 函数是一等公民究竟是个啥 无数次出现的函数是一等公民,到底讲的是啥?其实就一句话,函数能够作参数传递给变量不就得了。。。熟悉前端语言的其实应该很熟。ide

  4. 代码中无端出现+-等运算符 运算符重载在不少lib中能看到,像常常用的协程库就有

    public fun MainScope(): CoroutineScope = ContextScope(SupervisorJob() + Dispatchers.Main)
    复制代码

    咱们本身写的话,能够这么写

    fun main() {
        println(Foo(1, 2) + Foo(3, 4))
    }
    
    data class Foo(val x: Int, val y: Int) {
        operator fun plus(other: Foo): Foo = Foo(x + other.x, y + other.y)
    }
    复制代码

    运算符不少,能够参考https://www.jianshu.com/p/d445209091f0

  5. 炫技的中缀表达式 炫技的大哥们挺喜欢这么写

println("I" am "OK")
复制代码

或者一段中文style

println("你""干吗")
复制代码

这都是啥鬼哟,看看源码其实就好懂了,这就是一个简单的语法糖

infix fun String.am(any: Any) {
}
infix fun String.在(any: Any) {
}
复制代码

其实就是infix来修饰的中缀表达式,扩展了String,增长am方法,就是这么一回事而已,扩展函数搞不明白,后面会讲到(这是我最喜欢kotlin的一点了)

  1. DSL浪用 举例?要我这么懒的人来举例,怕是抬举我了,在网上找一个,
showDialog {
    title = "title"
    message = "message"
    rightClicks {
        toast("clicked!")
    }
}
复制代码

嗯嗯,这配置代码确实写得不多简洁了,可是对初学是一眼万年(懵),dsl的动态性和高扩展性,让kotlin有了更多的活力,可是DSL是要精而专,若是不到位的话,写起来也没轻松多少,固然,作公共组件,好比在这里使用dialog的话,当你熟悉了的话,调用会快得多。

其实DSL说穿了就是扩展函数搞起,就那回事,主要是你要想得全,封装得好

inline fun AppCompatActivity.showDialog(settings: CustomDialogFragment.() -> Unit) : CustomDialogFragment {
    val dialog = CustomDialogFragment.newInstance()
    dialog.apply(settings)
    val ft = this.supportFragmentManager.beginTransaction()
    val prev = this.supportFragmentManager.findFragmentByTag("dialog")
    if (prev != null) {
        ft.remove(prev)
    }
    ft.addToBackStack(null)
    dialog.show(ft, "dialog")
    return dialog
}
复制代码

上面这段代码扩展了Activity,因此咱们能够直接调用showDialog函数,里面惟一传入的参数是个lambda表达式,因此能够把{}直接放后面,别的都省略(lambda表达式后面立刻就会讲)。showDialog( )方法中只有惟一的参数settings,其类型是CustomDialogFragment.() -> Unit,即带有CustomDialogFragment参数类型的函数。

在showDialog( )方法内部,构造了CustomDialogFragment对象,并调用dialog.apply(settings)方法,其做用即在构造Dialog对象后,对Dialog进行设置。在实际调用showDialog()方法时,就能够持有该CustomDialogFragment对象,而后调用CustomDialogFragment提供的public接口配置Dialog。

说一说香在哪

若是有朋友听我喽嗖到这里的话,那么说明咱们都同样,是时候上硬菜了。

扩展库——协程

别给我整有的没有,我最开始香起来还就是协程了(手动滑稽)。

其实协程并非一个新概念,不少如今语言都是有滴,像python(人生苦短,我用python),go这些现代语言,都是有协程的,从计算机的发展史来讲,有可能协程的概念比线程还出来得要早。不过无所谓了,我也不当大历史家,如今咱们使用的协程通常是在线程上的,虽然协程也能够直接运行在进程上,和线程毛关系都没有,可是咱们作Android的,天生就有一条主线程。

不少人使用Rxjava看重的是啥?就是rxjava切换线程,顺滑如丝,那么,你若是只使用了rxjava的这一点特性的话,协程就足够了,性能还能直接提高很多。

概念太多,无非就是协程实际上就是极大程度的复用线程,经过让线程满载运行,达到最大程度的利用CPU,进而提高应用性能。咱们知道,线程阻塞时,其实这条线程是闲置的,经过协程,咱们能够在同一线程中运行多个协程任务,当这个协程任务挂起时,执行另外的协程,这样,线程就拉满了。

废话太多了,一哈讲不清,虽然有考虑后面会单独拎出来说,可是好歹要给口糖,说下简单使用。

由于是扩展库,因此须要手动引入

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
复制代码
GlobalScope.launch(Dispatchers.Default) {
    println(Thread.currentThread().name)
    withContext(Dispatchers.Main) {
        println(Thread.currentThread().name)
    }
}

//运行结果
I/System.out: DefaultDispatcher-worker-1
I/System.out: main
复制代码

这样就线程切换了,若是要在主线程等异步线程工做完后拿到它的返回值呢

GlobalScope.launch(Dispatchers.Main) {
    val job = async(Dispatchers.Default) {
        println(Thread.currentThread().name)
        "a"
    }
    println(job.await())
}

//控制台打印
I/System.out: DefaultDispatcher-worker-1
I/System.out: a
复制代码

ps:不推荐使用GlobalScope,可使用MainScope,至于缘由以及使用方法,之前再讲

一样,不一样协程前传递数据,也是可使用传递数据的,提供了channel很容易创建一个生产者——消费者模型

runBlocking {
        val channel = Channel<Int>(2)
        GlobalScope.launch {
            for (i in 1..5) {
                channel.send(i)
            }
            channel.close()
        }
        for (a in channel) {
            println(a)
        }
        println("done")
    }
复制代码

使用runBlocking,是由于必须channel运行在一个挂起suspend函数或者协程里,Channel<Int>(2)参数2是一次发送两,默认是一个一个发。

协程先就只讲这么多了,先看下别的香法吧

扩展函数

扩展函数香到什么地步,反正我是没法抗拒了。试问写java的童子们,曾几什么时候不都想扩展系统或者第三方类的方法时,只能含泪util,那么,有了扩展函数的特性后,妈妈不再用担忧我不能好好装B了。

inline fun String.lastChar(): Char = this[this.length - 1]

fun main(){
   println("abc".lastChar())
}

//////
// 输出c
复制代码

就是由于扩展函数的天马行空,才有了kotlin的自由自在,这时候我想到的是安迪在肖申克的高墙上,看着大伙享受着本身赢来的啤酒,这是自由的味道~

动态代理

什么,一句话就搞掂了动态代理?没错,就辣么轻松惬意

interface Base{
    fun abc()
}
class BaseImpl(val x:Int):Base{
    override fun abc() {
        print(x)
    }
}

class Derived(b:Base):Base by b

fun main(){
    val b = BaseImpl(110)
    Derived(b).abc()
}

// 控制台打印输出
// 110
复制代码

其中class Derived(b:Base):Base by b就帮咱们实现了动态代理

若是有兴趣能够反编译一下看java代码,其实kotlin是在编译里,会把咱们的代码翻译成静态代理的代码,由于没有使用反射,要比jdk动态代理的效率高得多,并且,这样的代码写起来不香么?

真泛型

看到真泛型时,脑壳瓜子是否是嗡嗡的,其实咱们在java中使用泛型时,是无法拿到这个泛型的类型的,JVM的泛型都是经过类型擦除来实现的,也就是说泛型类实例的实参在编译时被擦除,运行时不会被保留。kotlin运行在jvm上,同样会遇到这个问题,因此kotlin想了一个好方法,并解决了这个问题。

fun main(){
    testT<String>()
} 

inline fun <reified T> testT(){
    println(T::class.java)
}

////class java.lang.String
复制代码

能够看到,咱们直接拿到了传递进来的泛型类型,像Gson这些库,咱们就彻底能够从新封装一下了。

Lambda表达式

lambda是一个函数表达式,是匿名函数,用来表示函数类型的实例的一种方式。

这里要先了解一下高阶函数

来了来了,在kotlin中,函数是一等公民。

高阶函数是将函数用做参数或返回值的函数。

而lambda常常用在此处

Lambda 的语法规则是这样的:

  • lambda 表达式老是括在花括号中
  • 完整语法形式的参数声明放在花括号内,多参数的话用逗号隔开
  • 函数体放在 -> 以后
  • 若是须要返回值,那么函数体的最后一个表达式会被视为返回值

以以下几条规则可以让 Lambda 的表示更加简洁:

  • 若是参数类型能被推导出来,那么能够省略的类型标注

  • 若是 lambda 表达式的参数未使用,那么能够用下划线取代其名称

  • 若是 Lambda 只有一个参数而且编译器能识别出类型,那么能够不用声明这个参数并忽略 ->。 该参数会隐式声明为 it 。

  • 若是函数的最后一个参数接受函数,那么做为相应参数传入的 lambda 表达式能够放在圆括号以外

  • 若是该 lambda 表达式是调用时惟一的参数,那么圆括号能够彻底省略。

    仍是拿view的点击事件来举例,用kotlin的完整写法是:

    view.setOnClickListener(object : View.OnClickListener {
        override fun onClick(v: View?) {
            //TODO
        }
    })
    复制代码

    根据java中只有单个非默认抽象接口,在kotlin可使用函数来表示,这时,能够精简为:

    view.setOnClickListener({
        v: View -> //TODO
    })
    复制代码

    而lambda中惟一参数能够省略

    view.setOnClickListener({
       //TODO
    })
    复制代码

    lambda表达式为惟一参数时,()能够省略,这样就变成了

    view.setOnClickListener{
       //TODO
    }
    复制代码

    all啦

    其实lambda表达示只是在高阶函数中使用时,省略比较多咱们用不习惯而已,习惯以为挺爽的,不用再多写一堆代码了

小结

总的来讲,kotlin是越用越香的那种,最开始从java写kotlin还真的曲线陡峭,骂骂娘不就行了,世间万物,难逃真香定律。


个人CSDN

下面是个人公众号,欢迎你们关注我

相关文章
相关标签/搜索