简述:
上接上篇文章,今天我们来讲点Kotlin 1.3版本中比较时髦的东西,那么,今天就开始第二篇,看过一些大佬写关于Kotlin 1.3版本新特性的文章,基本上都是翻译了Kotlin Blog的官网博客。今天我不打算这么讲,既然今天的主题是时髦那就讲点有意思的东西。就像JetBrains开发者日上布道师Hali在讲Kotlin1.3新特性的时候完全就不用PPT的,拿起代码就是干。一起来看下今天提纲:
一、Coroutine协程转正,Kotlin/Native 1.0Beta
1、Coroutine协程的基本介绍
协程涉及内容很多,不是一句话就能讲清楚的,鉴于文章篇幅问题,所以这里不会去展开,后续会有专门系列文章去深入探讨。这里只是做个基本介绍。
协程转正
Kotlin 1.3版本最重要的莫过于协程转正,协程实际上在官方的库中早就有应用,但是由于API不稳定一直在Experimental中,如果你之前就玩过协程的话,对比正式版本Coroutine API上还是有那么一点变化的。协程实际上就是轻量级的线程,一个线程中可以执行多个协程。
协程作用
协程给开发者最大的感觉就是很好地解决了回调地狱的问题,在代码层面将异步的任务写成同步的调用形式。而且不会阻塞当前的线程。对于Android开发者而言,为了比较好地解决多请求回调的问题,我们自然就想到了RxJava。RxJava确实可以在一定程度下缓解回调地狱问题,但是还是无法摆脱使用回调的事实。那么协程将带你用一个全新的方式来解决这个问题.在Android开发中Kotlin的Coroutine协程是基本可以替代RxJava,并且此次大会上来自Google中国技术团队负责人就演讲关于Corouine在Android中的使用,并且还说不久Retrofit库将支持协程中的suspend函数。此外Jake Wharton 大神写了一个Retrofit支持协程Coroutine的adapter库retrofit2-kotlin-coroutines-adapter. 对于使用Kotlin开发Android的新项目来说,建议你不妨尝试使用Corouine来替代RxJava来用,也许你会觉得是真的爽啊。不信的话可以来看个协程在Android中应用的例子(伪代码):
可以看到以上涉及到了三个异步任务,然而只需要使用await函数挂起等待结果返回即可,根本就不需要callback回调,可以看到整个代码看起来都是同步的,实际上包含三个异步任务。可以看到协程使用起来还是很爽的。
3、解决回调地狱其他例子
不仅仅是Kotlin有这样的应用例子 ,如果你对JavaScript有所了解,JavaScript也是这样的,老版的JS执行异步任务一般用的是Promise或者ajax方式,一个异步执行后面总能带一个callback, 一旦涉及稍微复杂一点页面场景就出现了callback嵌套(回调地狱) 如果对ES6的语法新特性熟悉的话. 在Es6语法新增了async、await函数,也是将异步任务写成同步的调用,解决了JS中回调地狱的问题。
4、其他语言中的协程
协程不仅仅是Kotlin这门语言才有,相反在它出来之前老早就有了,诸如Python中的协程还有window编程的纤程,实际上和协程差不多。记得前段时间看到国外一篇文章说Java内部也在研究一个类似协程的轻量级线程框架项目(Loom),具体仍然还在试验阶段。想想Oracle这尿性估计得到Java 14之后吧,感兴趣的可以去具体了解下。
2、Kotlin/Native 1.0Beta
关于Kotlin/Native这里就不详细介绍了,感兴趣去参考我的上篇文章JetBrains开发者日见闻(一)之Kotlin/Native 尝鲜篇,里面有很多Kotlin/Native的详细介绍。
二、有意思的Contract 契约(Experimental)
看到标题有趣的contract契约,确实挺有趣也是非常实用的一个语法点。接下来一起看下Kotlin 1.3版本带来一个好玩的语法特性Contract契约,但是cotract还是在Experimental, API后期可能有变动,建议可以提前玩玩,但是先不要引入到生产环境中.
1、为何需要Contract契约
我们都知道Kotlin中有个非常nice的功能就是类型智能推导(官方称为smart cast), 不知道小伙伴们在使用Kotlin开发的过程中有没有遇到过这样的场景,会发现有时候智能推导能够正确识别出来,有时候却失败了。
不知道大家有没有去深入研究过这个问题啊,那么这里将会给出一个例子复现那种场景,一起来看下:
案例一
将鼠标移动到token.length中token上编译器就会告知你已经被smart cast处理过了
案例二
这时候的你突然想把判空处理检查放在一个checkTokenIsValid函数处理,在代码职责和可读性那块完全合情合理。但是如果你这么做了,神奇的事就悄然发生了。
你会发现token.length那报错了提示你进行判空处理,如下图所示,是不是一脸懵逼,实际上你在函数中已经做了判空处理啊。但是编译器说在它所知作用域内token就是个可空类型,所以就必须判空。这时候你就会觉得了哪里智能?
遇到上述的场景,相信很多人是不是都是使用!!来解决的啊。
案例三: 使用官方内置判断函数isNullOrBlank函数处理
使用Kotlin内置的函数去处理,你又会更加懵逼了…
看完这三个案例是不是一脸懵逼啊,同样都是定义成一个函数为啥我们自己的函数不能被识别智能推导,而Kotlin内置就可以呢,内置那个函数里面到底有什么黑魔法。于是我们似乎发现了问题的本质,就是对比一下isNullOrBlank函数实现有什么不同不就明白了吗?打开isNullOrBlank函数源码:
通过查看isNullOrBlank函数源码,似乎我发现了一个新的东西contract,没错这就是我们今天要分析的主角Contract 契约
案例四: 利用自定义contract契约让checkTokenIsValid函数具有被编译器智能推导识别的黑魔法
通过以上几个例子对比,是不是发现契约很神奇,貌似它有和编译器沟通说话方式,告诉它这里是smart cast,不要再提示我判空处理了。现在就揭开Contract神秘面纱,请接着往下看。
2、Contract契约基本介绍
基本定义
Kotlin中的Contract契约是一种向编译器通知函数行为的方法。就像上面所说那样,貌似它能告诉编译器此时它行为是什么。
基本格式
上述代码意思是:调用函数someThing并产生某种效果
是不是一脸懵逼,好抽象啊,不慌来一个实例解释一下:
3、Kotlin源码中Contract契约的应用
尽管在Contract契约目前还是处于Experimental状态,但是在Kotlin之前的版本标准库就已经大量使用Contract契约。包括上述例子所看到的isNullOrBlank函数就用到了契约,你可以找到1.2版本Kotlin源码中就能轻松找到。那么这里就举几个常见的例子。
CharSequence类扩展函数isNullOrBlank()、isNullOrEmpty()
契约解释:这里契约表示告诉编译器:调用isNullOrBlank()扩展函数产生效果是如果该函数的返回值是false,那么就意味着当前CharSequence实例不为空。所以我们可以发现一个细节当你调用isNullOrBlank()只有在取反的时候,smart cast才会生效,不信可以自己试试。
requireNotNull函数(这个函数相信大家都用过吧)
契约解释:这里可以看到和上面有点不一样,不带参数的returns()函数,这表示告诉编译器:调用requireNotNull函数后产生效果是如果该函数正常返回,没有异常抛出,那么就意味着value不为空
常见标准库函数run,also,with,apply,let(这些函数大家再熟悉不过吧,每个里面都用到contract契约)
契约解释:看到这个契约是不是感觉一脸懵逼,不再是returns函数了,而是callsInPlace函数,还带传入一个参数又是什么呢?
该契约表示告诉编译器:调用apply函数后产生效果是指定block lamba表达式参数在适当的位置被调用。适当位置就是block lambda表达式只能在自己函数(这里就是指外层apply函数)被调用期间被调用,当apply函数被调用结束后,block表达式不能被执行,并且指定了表示block lambda表达式只能被调用一次,此外这个外层函数还必须是个inline内联函数。
4、Contract契约背后原理(Contract源码分析)
看到上述Contract契约在源码中广泛的应用,并且看到上个例子分别代表三种不同类型的契约。此时的你是不是对Contract的源码顿时产生了浓厚的兴趣呢?下面我们将去简要剖析下Contract契约的源码。一说到分析源码很多人会脑袋疼,那是你可能没找到很好分析源码方法。
下面给出个人分析源码的一个习惯(一直沿用至今,效果感觉还是不错的):
源码分析的规则:需要分析一个源码问题,首先确定问题域,然后再列举出问题域中所有参与的角色或者名词概念,每个角色或名词所起的作用,角色与角色之间的关系,他们是如何通信的,如何建立联系的。
那么,我们就用这个原则一步步揭开Contract神秘面纱,让你对Contract的API有个更深全面的了解。
第一步,确定问题域(也就是你需要研究东西)
梳理和理解Contract契约背后原理,以及它的工作流程
第二步,确定问题域中参与角色(也就是Contract中那些API类),先给出一个contracts package中所有类和接口
第三步,理清它们各自职责。
然后重点来看下ContractBuilder.kt文件,这个实际上是Contract契约一个DSL Builder以及暴露给最外面一个contract函数.
第四步,理清Effect之间关系。
第五步,分析一个例子模拟contract工作流程
其实分析完后发现契约描述的实际上就是函数的行为,包括函数返回值、函数中lambda表达式形参在函数内部执行规则。把这些行为约束告知给编译器,可以节省编译器智能分析的时间,相当于开发者帮助编译器更快更高效做一些智能推导事情。
5、自定义Contract契约
实际上自定义在上述例子中早就给出来,由于在Kotlin1.3版本Contract还处于实验阶段,所以不能直接使用。看了源码中各种契约使用例子,相信自定义一个契约应该很简单了。
其实说了那么多,大家有没有体会到一点东西,契约实际上就是开发者和编译器之间定义一个协议规则,开发者通过契约去向编译器传递一些特殊效果Effect。而且这些效果都是针对函数调用的行为来确定的。所以从另一方面也说明了开发者必须足够了解业务场景才能使用契约,因为这相当于编译器把一些操作信任于开发者来处理,开发者使用空间灵活度越高,那么危险性也越大,切记不能滥用。
6、Contract契约使用限制
虽然Kotlin契约看起来很棒,但目前的语法目前还不稳定,并且未来API可能会有改变。不过有了上述一系列分析,就算将来变换了,你也能很快理解。
1、我们只能在顶层函数体内使用Contract契约,即我们不能在成员和类函数上使用它们。
2、Contract调用声明必须是函数体内第一条语句
3、就像我上面说得那样,编译器无条件地信任契约;这意味着程序员负责编写正确合理的契约。
尽管Contract还处于实验阶段,但是我们也看到了在很久之前版本中stalib标准库源码中就大量使用了契约,所以预测在后续版本中API改动也不会很大,所以这时候深入分析还是值得的。
三、结语
由于Contract契约深入分析一下,占用文章篇幅过大。所以其他新特性相关介绍和分析挪到了下篇文章,欢迎持续关注~~~。
Kotlin系列文章,欢迎查看:
原创系列:
JetBrains开发者日见闻(一)之Kotlin/Native 尝鲜篇
教你如何攻克Kotlin中泛型型变的难点(实践篇)
教你如何攻克Kotlin中泛型型变的难点(下篇)
教你如何攻克Kotlin中泛型型变的难点(上篇)
Kotlin的独门秘籍Reified实化类型参数(下篇)
有关Kotlin属性代理你需要知道的一切
浅谈Kotlin中的Sequences源码解析
浅谈Kotlin中集合和函数式API完全解析-上篇
浅谈Kotlin语法篇之lambda编译成字节码过程完全解析
浅谈Kotlin语法篇之Lambda表达式完全解析
浅谈Kotlin语法篇之扩展函数
浅谈Kotlin语法篇之顶层函数、中缀调用、解构声明
浅谈Kotlin语法篇之如何让函数更好地调用
浅谈Kotlin语法篇之变量和常量
浅谈Kotlin语法篇之基础语法
翻译系列:
[译]Kotlin的独门秘籍Reified实化类型参数(上篇)
[译]Kotlin泛型中何时该用类型形参约束?
[译] 一个简单方式教你记住Kotlin的形参和实参
[译]Kotlin中是应该定义函数还是定义属性?
[译]如何在你的Kotlin代码中移除所有的!!(非空断言)
[译]掌握Kotlin中的标准库函数: run、with、let、also和apply
[译]有关Kotlin类型别名(typealias)你需要知道的一切
[译]Kotlin中是应该使用序列(Sequences)还是集合(Lists)?
[译]Kotlin中的龟(List)兔(Sequence)赛跑
[译]Effective Kotlin系列之考虑使用静态工厂方法替代构造器
[译]Effective Kotlin系列之遇到多个构造器参数要考虑使用构建器
实战系列:
用Kotlin撸一个图片压缩插件ImageSlimming-导学篇(一)
用Kotlin撸一个图片压缩插件-插件基础篇(二)
用Kotlin撸一个图片压缩插件-实战篇(三)
浅谈Kotlin实战篇之自定义View图片圆角简单应用
欢迎关注Kotlin开发者联盟,这里有最新Kotlin技术文章,每周会不定期翻译一篇Kotlin国外技术文章。如果你也喜欢Kotlin,欢迎加入我们~~~
领取专属 10元无门槛券
私享最新 技术干货