给Java程序员的Kotlin介绍信

     Kotlin已出现一段时间,很多同学都听过甚至写过一些demo。在我入门时候总有一种盲人摸象不识大体的感觉,如果恰好你也有这种感觉,那么就一起探讨下面的问题吧:
     What is Kotlin?– Kotlin是什么?
     Why Kotlin?–Kotlin能给我们带来什么?

   一、Kotlin是什么

    Kotlin是一种Java系编程语言。

   Java系编程语言指经过编译生成字节码(Java byte code), 从而可在JVM上运行的语言,也可以称之为Java平台语言。

    注意:Android 中的虚拟机并不是 JVM,而是Dalvik/ART,需要用字节码(Java byte code)进行再一次转换成相对应的Dalvik字节码。

   提问: 是否可以新创造一门语言,编译的时候也生成字节码,然后在JVM 中运行呢?这样既能享受到 JVM 和成熟 Java 框架的各种好处,还可以甩掉Java语言的不足之处,有很多自己新的特性。

   回答: 当然可以 !!比如Java 平台已经衍生出 Scala、Clojure、Groovy 等比较流行的语言了。而 Kotlin 则是Java平台系语言中的新星,出自大名鼎鼎的JetBrains 公司。

   下面用一个图来帮助我们了解下Java与Kotlin的编译与执行:

《给Java程序员的Kotlin介绍信》 Java VS Kotlin.jpg

名词解释(大神请略过)

Java source code : Java源代码,就是我们根据Java 语言规范所编写的源程序文件,扩展名为.java。
Kotlin source code: Kotlin源代码,就是我们根据Kotlin语言规范所编写的源程序文件,扩展名为.kt。
Javac: 全称Java compiler,是收录于JDK中的Java语言编译器。该工具可以将后缀名为.java的源文件编译成后缀名为.class的文件,该文件包含着可以运行于Java虚拟机的字节码。
Kotlinc:全称Kotlin compiler,该工具可以将后缀名为.kt的源文件编译成后缀名为.class的文件,该文件包含着可以运行于Java虚拟机的字节码。感兴趣同学可以继续研究Kotlinc用法
    可以在Android Studio->File->Settings->Plugins->Kotlin 看到Kotlin插件,它就是Android开发环境下的Kotlin编译器。
Java byte code: Java字节码,是Java虚拟机的(JVM)的指令集、存储于.class文件中、可以用Javap等命令查看。
JVM: Java Virtual Machine,缩写为JVM,一种能够运行Java bytecode的虚拟机,JVM屏蔽了与具体操作系统平台相关的信息,使得Java程序只需编译生成可以在Java虚拟机上运行的目标代码(Java字节码),就可以在多种平台上不加修改地运行。
      由于JVM并不是专为Java所实现的运行时,所以只要有编程语言的编译器能生成正确Java bytecode文件,则这个语言也能实现在JVM上运行,比如我们本文主角Kotlin。
机器码: Machine code是一种指令集,这种指令集是计算机的CPU可直接解读懂的数据,比如0000代表加载(LOAD)0001代表存储(STORE)。
dx工具: Android Apk打包生成的过程,就是将.java文件转换成.dex文件的过程。其中dx.bat就是将.class文件转换为.dex文件的工具,在类似Android\Sdk\build-tools\28.0.3目录下可以找到。
Dalvik: 类似JVM,可以称为Android虚拟机,后缀为.dex(即“Dalvik Executable”)格式的Java应用程序可以在其上运行。.dex格式是专为Dalvik设计的一种压缩格式,适合内存和处理器速度有限的系统。
ART: Android Runtime,在Android 5.0及后续Android版本中作为正式的运行时库取代了以往的Dalvik虚拟机。它与Dalvik的主要不同在于采用Ahead-of-time(AOT)技术,那就是在安装Apk时就把字节码转换为机器码,Dalvik是在运行时候才把字节码转换为机器码,所以ART有者更好的运行时性能,但是有着安装时间更长、占用存储空间更大的劣势。

第一章小结与问题

      从上文可以看出创造Kotlin语言最主要工作就是是创造一种新的编译器Kotlinc,该编译器主要作用是把符合Kotlin约定语法与结构的源代码解析为Java字节码,从而可以在成熟的Java平台上运行。
      那么我们这里留下一些问题给读者:
      Java源码、Kotlin源码和字节码是否是一一对应,为什么这样设计?
      Java源码和Kotlin源码是否能够利用工具实现完全转换,如果可以原理是什么,如果不可以,哪些地方不可以转换,原因是什么?

二 相对于Java,Kotlin给我们带来哪些好处

1 Data class -类定义标准与简化

Data class是Kotlin发布时候用的标题,其实指类定义的标准与简化。
例如我们要写一个学生类,有两个成员变量 姓名和分数,java和Kotlin代码如下:

《给Java程序员的Kotlin介绍信》 java class define.png
《给Java程序员的Kotlin介绍信》 Kotlin define class.png

上面Java和Kotlin定义作用上是等同的,我们可以看到Kotlin是多么的简洁,编译器直接帮我们补充好、set()、get()、toString()、hashcode()等方法。我们利用工具反编译Kotlin生成的apk得到,刚才那句话经过编译器得到是下面的内容(注 只贴了一部分)

《给Java程序员的Kotlin介绍信》 Kotlin类反编译.png

《给Java程序员的Kotlin介绍信》 Kotlin反编译续.png

可以看到Kotlin编译器自动帮我们生成了构造函数、copy()、get()、set()等方法。

2 Extension Functions-类功能扩展

类扩展可以帮助我们扩展已有的 Class,而无需继承这个 Class,无论我们能不能访问源码。这对于系统 Class 以及一些第三方 library 中的 Class 特别有帮助。例如

《给Java程序员的Kotlin介绍信》 Kotlin Extension Functions.png

我们扩展Int 类,让Int类多出来一个方法getNext(),然后我们在我们的工程里面都可以使用这个方法。那么有同学就会想是否有大神把一些常用的扩展封装成库给我们,恰好Google也做了,Jetpack -这个里面就包含一些常用的类扩展功能、感兴趣的同学可以去研究下。

3 Null Safety-空指针检查

Kotlin 中允许定义变量时可以指定它为可空类型(Nullable Type)和不可空类型(Non-Null Type),默认是不可空类型。

《给Java程序员的Kotlin介绍信》 Kotlin null check.png

4 Coroutines-协程

Coroutines: Co -合作+ routine-程序 -》Coroutines=合作的子程序集合,协程的诞生是为了解决子程序间合作与调度的问题。

协程定义理解

      子程序间合作问题最主要难点在于异步与回调。比如有两个程序A-下载图片,B-显示图片,流行处理方法就是开启一个线程去下载图片、下载成功后通知在UI线程中的程序B运行显示图片,这里我们需要自己写开启线程、接口回调代码、更不要说去考虑线程数量、切换代价等。而有了协程之后,它的内部库会封装异步操作、回调、订阅等,使各个子程序在不同线程上调度执行,而代码则可以写的保持如同顺序执行一样简单。
      所以我们可以认为协程是一种特殊的子程序集,它可以在一个子程序中中断,去执行其它子程序,不是函数调用,有点类似于CPU的中断。
下面用一幅图来形象的帮助我们理解进程、线程与协程的关系

《给Java程序员的Kotlin介绍信》 进程、线程、协程.png

形象映射

生产车间:计算机中CPU等硬件资源是有限的,每个CPU单一时间只能运行一个线程。所以我们可以把CUP等硬件资源当做是正在生产中的车间,车间里面放置着用于生产的原材料和组装好的正在进行生产的产线;为了方便理解、假设我们的资源非常有限,只有一个车间、车间内只能开动两条生产线(双核CPU)。

车间搭建资源:车间想持续产出,不仅需要着用于生产的原材料、还要有搭建好的产线。车间搭建的资源就是在仓库中堆积着的原材料、设备与搭建方案文案、一旦决定生产何种产品,那就从仓库中取出这这些资源,按照搭建方案把设备组装成产线、放置好原材料,通上电开始生产。车间搭建资源就相当于进程
      每一个车间搭建资源(进程)都是要占用仓库不少位置的,所以仓库越大(存储容量越大)就可以有更多的进程、对应手机就是存储越大可以安装APP更多。
      因为不同车间搭建资源只有在装载在车间中实际生产运行时候才是活动的,所以他们(进程)想进行交流通信,就是建立一个与车间无关的公共区域、这些车间搭建资源可以在他们运行时候都去该区域放东西或者拿东西,实现资源的交换。用计算机专业术语讲讲就是通过共享内存实现进程间通信。

车间选择(切换):把当前方案所需的原材料拿掉换上新的原材料、把当前产线拆掉,再组装新的产线。相当于进程切换,进程切换=原料更换+产线重建

产线:每条产线可以通过调整参数、原材料进行不同的产品加工、相当于线程。

产线切换:拆掉原来的产线、根据新的产线方案来重组新的产线,相当于线程切换;

场景切换器:控制并实施车间和产线切换的部门,相当于操作系统的Kernel(内核)。我们需要在资源有限的情况下实现利益最大化,但几条产线的产品具有相互依赖性并且外部对产品的需求一直也在变化中,那么如何根据外界环境变化最优的安排不同的产线与生产方案,正是kernel考虑的一个很重要问题,调度算法。

任务控制流:一系列生产任务(用户添加)并且有内置的调度机制,该机制可以控制决定自己每一个子任务在哪条加工线运行,并且按照顺序执行,相当于协程
      任务控制流中每个黑色的箭头代表一个子任务,任务控制流,可以控制子任务暂停执行,开始执行、并可以安排每个子任务在不同生产线上完成,这个相当于协程切换,具体协程切换如下:
1 保存当前协程的上下文(运行栈,返回地址,寄存器状态);
2 设置将要唤醒的协程的入口指令地址到IP寄存器;
3 恢复将要唤醒的协程的上下文。

发现与思考

切换代价:进程切换>线程切换>协程切换
      进程切换相当于重新把当前车间所有东西搬走,按照新的车间搭建方案重新搭建车间和产线,需要搬运、摆放原材料、写生产小结、重新组装原材料。
      线程切换相当于新组装一个产线,因为新产线需要的各种资源,已经取出来在车间内,所以只需要把不需要的产线拆卸掉,重新组装新的产线就好,无需搬运生产资料,如果当前产线比较少,甚至不需要拆卸产线,直接组装新产线就好。
      协程切换相当于内部的任务调度,产线无需拆卸重组,只是调整一些参数就可以运行不同的任务, 我们还可以看到协程是自己控制,非操作系统kernel控制。

进程、线程、协程限制: 车间有限,所以进程有限,在当前只有一个;线程在车间内,创建线程也需要各种资源、车间大小也有限,所以线程也有限;协程,原则上只是一种任务调度机制,大部分以文档形式存在,所需要的资源都是线程的,所以限制很少,可以创建很多很多协程。

协程挂起: 任务控制流发现它控制的任务A需要的材料不足,那么它就说先暂停这个子生产任务,但生产线可以继续工作,把A需要的材料生产出来A再继续工作。

协程调度: 从上面可以知道,协程有两个重要的部分1 子程序集 2 调度机制。调度机制是协程不同于子程序的关键。下面简单说下协程的调度器:
      CoroutineDispatcher,协程调度器,决定协程所在的线程或线程池。它可以指定协程运行于特定的一个线程、一个线程池或者不指定任何线程(这样协程就会运行于当前线程)。coroutines-core中 CoroutineDispatcher 有三种标准实现Dispatchers.Default、Dispatchers.IO,Dispatchers.Main和Dispatchers.Unconfined,Unconfined 就是不指定线程。
      launch函数定义如果不指定CoroutineDispatcher或者没有其他的ContinuationInterceptor,默认的协程调度器就是Dispatchers.Default,Default是一个协程调度器,其指定的线程为共有的线程池,线程数量至少为 2 最大与 CPU 数相同。

协程原理

      从上面可以看出,协程主要作用解决不同子程序调度问题,它会封装异步操作、回调、订阅等,使我们的程序在不同线程上调度执行,而代码则可以写的保持如同顺序执行一样。如果这种事是我们自己来做呢?其实我们也很容易想到设定一个全局的变量,然后不同的协作任务都能改变这个变量的状态,然后根据变量的状态值,来决定调度哪一个子程序,这叫Switch状态机,协程也是这样做的,总结下就是状态机+回调=协程调度。感兴趣同学可以阅读协程原理解析

协程与RxJava

      功能类似、实现不同,协程写法更具有可阅读性,协程具体实现也更高效,感兴趣同学可以看搜关键字RxJava与协程,多看几篇。

其他注意事项

顶层方法定义

《给Java程序员的Kotlin介绍信》 顶层方法

在kotlin中方法是可以独立于类的,我们可以像上图一样定义方法,然后全局调用,默认方法都是public。

Kotlin lambda

阅读Kotlin源码时候,会遇到lambda表达式,kotlin中对于lambda有很多简化的约定:
1 如果lambda表达式是函数调用的最后一个实参,它可以放在括号外面;
2 当lambda是函数唯一的实参时,可以去掉函数调用的括号;
3 如果lambda的参数的类型可以推导,那么可以省略参数的类型;
4 对于lambda中一个参数时,可以使用默认参数名称it来代替命名参数,并且lambda的参数列表可以简化,省略参数列表和->。

Kotlin to JS

提供插件kotlin2js 将Kotlin代码转换为JavaScript 代码,这样Kotlin就不仅仅是一种Java平台语言、在跨平台上走的更进一步。

第二章小结与Kotlin启示

      Kotlin语言为解决Java语言的问题而生,它提供了类的标准定义与简化、类的方法扩展、空指针检查、协程等用户(工程师)友好的功能,这些都是把用户经常需要做和必须做的工作抽取出来提供标准化和自动化的解决方案,从而减少用户的工作量,这属于AI思维。
      AI思维是通过把重复、规律的事交给人工智能来成降低成本,本质是标准化和量化思维! 当一件事情可以标准化和量化,我们便能够实现可复制的成功

参考文献

Kotlin中文学习网站 http://www.kotlincn.net/docs/reference/
协程理解:https://www.jianshu.com/p/2979732fb6fb

    原文作者:Simple_world
    原文地址: https://www.jianshu.com/p/e14eb017c545
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞