近半年因業(yè)務(wù)需要,帶領(lǐng)團(tuán)隊(duì)成員新開發(fā)一款A(yù)PP并順利上線,目前已迭代2個(gè)版本。眾所周知,2019年Google I/O大會(huì)上宣布Kotlin-first,因?yàn)轫?xiàng)目最開始,我們便制定了APP全部代碼由Kotlin實(shí)現(xiàn)的目標(biāo)。項(xiàng)目告一段落后,因此整理一篇文章,一是在團(tuán)隊(duì)中分享給更多成員,二是鞏固已有知識(shí)。前言結(jié)束~
一. Kotlin語言的發(fā)展歷史
Kotlin是一門在JVM上運(yùn)行的靜態(tài)類型編程語言,也可以被編譯成為JavaScript源代碼。由JetBrains開發(fā)(1200+員工)。"Kotlin"命名取自圣彼得堡附近的一座島嶼名稱。Kotlin是根據(jù)Apache 2.0授權(quán)的免費(fèi)開源項(xiàng)目:JetBrains/kotlin
- 2011年7月,JetBrains推出Kotlin項(xiàng)目(已開發(fā)1年之久)
- 2012年2月,JetBrains以Apache 2許可證開源此項(xiàng)目
- 2016年2月,Kotlin v1.0發(fā)布
- 2017年5月,Google在I/O大會(huì)上宣布:
Kotlin為Android開發(fā)一級(jí)語言 - 2019年5月,Google在I/O大會(huì)上宣布:
Kotlin-first,成為Android開發(fā)首選語言
Android Studio新建Project默認(rèn)語言是Kotlin.png - 2020年3月,發(fā)布最新版本v1.3.71
JetBrains團(tuán)隊(duì)創(chuàng)建Kotlin項(xiàng)目的主要目標(biāo):
- 創(chuàng)建一種兼容Java的語言
- 編譯速度至少同Java一樣快
- 比Java更安全
- 比Java更簡(jiǎn)潔
為什么Kotlin會(huì)得到Google支持?
- 與Oracle曠日持久的Java侵權(quán)案:最新結(jié)果Google敗訴,需向Oracle賠償88億美元。起因:Oracle起訴Android中無償使用了
37個(gè)Java APIs,侵犯專利;同時(shí)有9行代碼抄襲了Java - Kotlin自身的語言優(yōu)點(diǎn)
Android官方Kotlin示例:

Kotlin構(gòu)建的應(yīng)用:

Stack Overflow Developer Survey 2019的結(jié)果中:
- 最受喜愛的編程語言排名
第4,72.6%(Top 1:Rust 83.5%,Java排名第18:53.4%) -
6.4%的人使用Kotlin編程 (Top 1:JavaScript 67.8%,Java排名第5:41.1%)
二. Kotlin適用范圍

重點(diǎn)解釋下服務(wù)端、Web開發(fā)、Android:
1. 服務(wù)端:
- Spring、Vert.x、Ktor、kotlinx.html等均對(duì)Kotlin提供支持
- 可伸縮性:Kotlin 對(duì)協(xié)程的支持有助于構(gòu)建服務(wù)器端應(yīng)用程序, 伸縮到適度的硬件要求以應(yīng)對(duì)大量的客戶端
協(xié)程:一個(gè)線程在執(zhí)行函數(shù)時(shí),如果遇到如I/O等阻塞操作,線程可以主動(dòng)控制,去操作其他函數(shù),等I/O等阻塞操作完成,再回來繼續(xù)執(zhí)行原函數(shù)。是一種“偽多線程”,無需線程上下文切換的開銷,效率高
2. Web開發(fā):
- Kotlin可編譯為JavaScript
- 支持與DOM元素交互、支持與圖形(如WebGL)交互、支持JQuery和ReactJS等第三方庫和框架、兼容CommonJS等
3. Android:
- Android官方支持Kotlin
- 舉例:Keepsafe的App Lock應(yīng)用已100%轉(zhuǎn)換為Kotlin,源代碼行數(shù)減少
30%,方法數(shù)減少10%
三. Kotlin VS Java
相比Java,Kotlin的優(yōu)點(diǎn):
1. 代碼簡(jiǎn)潔
經(jīng)典例子:


以上針對(duì)Person類,Java和Kotlin的實(shí)現(xiàn)一致,但Kotlin代碼行數(shù)減少24行。
Kotlin默認(rèn)幫類的成員變量設(shè)置了getter、setter方法,實(shí)現(xiàn)是:
var id: String? = null
set(value : String?) {
id = value // field = value
}
// 以上代碼有一個(gè)bug,你發(fā)現(xiàn)了嗎?如果使用id=value,會(huì)造成set函數(shù)的無限遞歸調(diào)用,最終導(dǎo)致stackoverflow,在Kotlin中默認(rèn)setter方法中需使用field代替類成員變量,避免set的默認(rèn)調(diào)用
另外簡(jiǎn)潔性,可參考對(duì)比示例:from-java-to-kotlin
2. 安全性強(qiáng):改善空指針問題
- Top 5 Crashes on Android,Top 1 NullPointerException
- 圖靈獎(jiǎng)得主Tony Hoare在1965年設(shè)計(jì)ALGO W編程語言時(shí)引入Null引用,2009年,QCon大會(huì)上Tony稱這是"
十億美元的錯(cuò)誤"
由上可知空指針問題的嚴(yán)重性,而Kotlin天然的創(chuàng)造性提出可空與非空類型,代碼階段就很好減少空指針問題出現(xiàn)的可能性
3. 互操作性:充分利用JVM、Android和瀏覽器現(xiàn)有庫
與Java代碼完全可互相操作,.java文件、.kt文件都是編譯為.class文件,不過編譯過程還是有區(qū)別,編譯前端(詞法分析/語法分析/語義分析/中間代碼生成)與Java基本一致,編譯后端有區(qū)別(做了很多代碼封裝工作,例如自動(dòng)生成Getter/Setter方法等,將代碼層的很多封裝工作轉(zhuǎn)移到編譯后端,這也是Kotlin語言簡(jiǎn)潔的原因)
4. 工具友好:IntelliJ IDEA/Adroid Studio/Eclipse等支持
最后,從語法上舉幾個(gè)明顯的差異例子:
- Kotlin創(chuàng)建對(duì)象無需
new關(guān)鍵詞 - Kotlin每行代碼后無需加"
;"分號(hào) - Kotlin
主動(dòng)推斷變量類型(var/val),無需特別聲明 - Kotlin的文件類型是
*.kt - Kotlin方法定義要加關(guān)鍵詞
fun
更多與Java的語言特性比較:參考 http://shouce.jb51.net/kotlin/txt/comparison-to-java.html
四. 語法糖
1. 變量
1.1 Kotlin是一種靜態(tài)類型的語言,編譯器根據(jù)所賦值的類型來推斷類型,類型在編譯時(shí)解析確定且從不改變。聲明變量的2個(gè)關(guān)鍵字:var、val
var 變量值可以更改
val 變量值賦值后不能更改
注意:
var a // 局部變量編譯錯(cuò)誤:The variable must either have a type annotation or be initialized
改為:
var a : String //正確
var a = "test" //正確
1.2 Kotlin中類型是默認(rèn)的非空值,如果變量是一個(gè)可空類型,聲明時(shí)需要添加"?",例如:
var a : String? = null // 正確
var a : String = null // 編譯錯(cuò)誤
備注:某種程度上,你可以認(rèn)為非空String 和 可空String是兩個(gè)不同類型
1.3 如果變量是可空類型,調(diào)用時(shí)必須增加"?"空檢查,例如:
println (a?.length) // 正確,等于: if (a != null) println(a.length);
println (a.length) // 編譯錯(cuò)誤
1.4 雙嘆號(hào)!!表示在對(duì)象不為空的情況下執(zhí)行,如果對(duì)象為空,則執(zhí)行時(shí)拋出NullPointerException,例如:
val a : String ?= null
a!!.length;
1.5 類的成員變量需聲明時(shí)初始化,或聲明abstract,或者init函數(shù)中初始化,或者使用lateinit var延遲初始化。另外延遲初始化還可以使用by lazy,區(qū)別是:lateinit var僅支持類成員變量,要求變量是var;by lazy支持類成員變量/局部變量,要求變量是val
private var a : String? // 編譯錯(cuò)誤
private lateinit var a : String? // 正確
private abstract var a : String? // 正確
private var a : String?
init { a = null } // 正確
備注1:這里也是與Java語法區(qū)別,Java中類成員變量可不初始化,有默認(rèn)值
備注2:Kotlin提供了isInitialized函數(shù)來判斷變量是否已初始化/賦值
1.6 類的成員變量有這4個(gè)可見性修飾符:private、 protected、 internal和public。 如果沒有顯式指定修飾符的話,默認(rèn)可見性是 public。備注:private < protected < internal < public,其中internal指類聲明的本模塊內(nèi)可見:
模塊是指編譯在一起的1套 Kotlin 文件,可以是:
- 1個(gè) IntelliJ IDEA 模塊
- 1個(gè) Maven 項(xiàng)目
- 1個(gè) Gradle 源集(例外是 test 源集可以訪問 main 的 internal 聲明)
- 1次 <kotlinc> Ant 任務(wù)執(zhí)行所編譯的一套文件
2.條件語句支持if-else、when。when表達(dá)式的每一個(gè)分支由一個(gè)條件、一個(gè)箭頭(→)和一個(gè)結(jié)果來表示
3. 標(biāo)準(zhǔn)函數(shù):let、with、run、apply、also
let函數(shù):
object.let{
it.todo() // 函數(shù)內(nèi)it代替object,可訪問其屬性和方法
}
使用場(chǎng)景:
場(chǎng)景一:最常用的場(chǎng)景就是使用let函數(shù)處理需要針對(duì)一個(gè)可null的對(duì)象統(tǒng)一做判空處理
object?.fun1()
object?.fun2()
使用let函數(shù)后:object?.let {
it.fun1()
it.fun2() }
場(chǎng)景二:然后就是需要去明確一個(gè)變量處特定的作用域范圍內(nèi)可以使用
with、run、apply、also等函數(shù):
with(object) { // object作為函數(shù)參數(shù),函數(shù)塊內(nèi)可使用this代替object,返回值為最后一行或return指定
// todo
}
object.run{ // run相當(dāng)于let和with結(jié)合體
// todo
}
object.apply{ // apply和run很像,區(qū)別是返回值不一樣,apply函數(shù)返回傳入的對(duì)象本身
// todo
}
object.also{ // also和let很像,區(qū)別是返回值不一樣,also函數(shù)返回傳入的對(duì)象本身
// todo
}
4. 伴生對(duì)象 companion object,與類綁定的一個(gè)單例對(duì)象(編譯后.class中轉(zhuǎn)換成static對(duì)象),例如:
class MyClass {
companion object {
val logger = LoggerFactory.getLogger(MyClass::class.java)
}
}
5. 一些注解:
5.1 @JvmOverloads:在有默認(rèn)參數(shù)值的方法中使用此注解,此方法會(huì)暴露多個(gè)方法
@JvmOverloads fun f(a: String, b: Int=0){ }
相當(dāng)于:
fun f(a: String){
b = 0;
// ...
}
fun f(a: String, b: Int){}
五. Google建議使用Kotlin最佳實(shí)踐
- 側(cè)重于可讀性,而不是盡量縮短代碼行。用 Kotlin 語法糖很容易過度。
- 確立最適合自己團(tuán)隊(duì)的編碼規(guī)范和慣用語。
六. 參考
- from-java-to-kotlin
- Android Kotlin官網(wǎng)
- FQA - Kotlin語言中文站
- Stack Overflow Developer Survey 2019
- Kotlin語言中文站
- Kotlin 勢(shì)必取代 Java?
- Kotlin 編譯之路 "Kotlin編譯器"
作者:kevin song,2020.6.22于南京建鄴區(qū)
