以小白眼光看kotlin前端
kotlin这个小丫环被谷歌扶正为大房两年,期间看过很多博文,不少人也已经把我的项目迁移到kotlin了,固然国外的开发者更给力,直接大部企业开发已经kotlin,也订阅了涛哥的极客时间(一直没时间看,果真看视频太费事,仍是文章能够抽得时间挤一挤学),一直是不想学习啊,一我的的惰性就是这样,java又不是不能用,代码通俗易懂。说句内心话,java8的lambda表达式我都没学,ps:不过好像用java8 lambda表达示的在工做中也没怎么碰到。。java
第一眼看到kotlin,大部分人都是,哇,我被它惊艳了(至少表面上都这么说的),然而本渣狗并无感受(难道就我一个菜狗?),反而以为:啥?为了一个空安全学新的知识,if(x!=null)它不香哦。直到最近换了工做,遗留代码一半是kotlin,糟了,是心动的感受。(不得不上手写了)python
其实最开始上手依然痛苦无比,lambda表达式各类省略,函数是一等公民这句废话几乎每一个博客都写了,可是我看着太抽象啊,我管你几等公民,你告诉我它区别在哪啊,有啥用啊。android
空安全并无让我香到学习新知识安全
空安全确实没有香到非学新东西不可,不过,它的设计思路仍是不错的。判空无非两种作法,a. 在编写代码里运行时避免空指针异常,也就是经常使用的if(x!=null);b. 直接静态代码检查,在编译期就避免了空指针,kotlin其实就是这种了,好比定义为String,那么很差意思,别的地方你传个null,编译器就给你整红,非要可空,那后果你本身负,用String?吧。这样的好处是省略了大把逻辑判断,让编译器帮咱们把把关。app
Lambda表达式省略一时爽,不会写的人看不懂异步
最简单的也是出现最多的,莫过于点击事件了jvm
//kotlin
view.setOnClickListener {
//TODO
}
//java
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
复制代码
你们其实看不习惯的缘由就在于,没有()带参数,那我{}里要使用()传递过来的参数怎么办,其实编译器如今已经很智能的告诉咱们了 async
函数是一等公民究竟是个啥 无数次出现的函数是一等公民,到底讲的是啥?其实就一句话,函数能够作参数传递给变量不就得了。。。熟悉前端语言的其实应该很熟。ide
代码中无端出现+-等运算符 运算符重载在不少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
炫技的中缀表达式 炫技的大哥们挺喜欢这么写
println("I" am "OK")
复制代码
或者一段中文style
println("你" 在 "干吗")
复制代码
这都是啥鬼哟,看看源码其实就好懂了,这就是一个简单的语法糖
infix fun String.am(any: Any) {
}
infix fun String.在(any: Any) {
}
复制代码
其实就是infix来修饰的中缀表达式,扩展了String,增长am方法,就是这么一回事而已,扩展函数搞不明白,后面会讲到(这是我最喜欢kotlin的一点了)
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是一个函数表达式,是匿名函数,用来表示函数类型的实例的一种方式。
这里要先了解一下高阶函数
来了来了,在kotlin中,函数是一等公民。
高阶函数是将函数用做参数或返回值的函数。
而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还真的曲线陡峭,骂骂娘不就行了,世间万物,难逃真香定律。
下面是个人公众号,欢迎你们关注我