1. 程式人生 > >為什麽我要改用Kotlin

為什麽我要改用Kotlin

android android開發 Kotlin 語言

寫在前面的話,作為一個不熬夜的人,一覺醒來發現Kotlin成為了Android的官方語言,可謂是大喜過望。為了趁熱打鐵,我決定提前三天放出原定本周日Release的文章。希望能及時讓大家了解一下Kotlin。

相信很多開發人員,尤其是android開發者都會或多或少聽說過Kotlin,當然如果沒有聽過或者不熟悉也沒有關系。因為本篇文章以及博客後期的內容會涉及到很多關於Kotlin的知識分享。

在寫這篇文章前的一個多月,Flipboard中國的Android項目確定了正式將Kotlin作為項目開發語言,這就意味著新增的代碼文件將以Kotlin代碼格式出現,而且同時舊的Java代碼也將會陸陸續續翻譯成Kotlin代碼。在使用Kotlin的這段時間,被它的簡潔,高效,快捷等等特點震撼,所以有必要寫一篇文章來談一談Kotlin的特性,如若能取得推廣Kotlin的效果則倍感欣慰。

Kotlin的“簡歷”

來自於著名的IDE IntelliJ IDEA(Android Studio基於此開發) 軟件開發公司 JetBrains(位於東歐捷克)
起源來自JetBrains的聖彼得堡團隊,名稱取自聖彼得堡附近的一個小島(Kotlin Island)
一種基於JVM的靜態類型編程語言
來自知名的工具開發商JetBrains,也就決定了Kotlin的基因中必然包含實用與高效等特征。那我們接下來看一看Kotlin的特點,當然這也是我改用Kotlin的重要原因。

語法簡單,不啰嗦

//variables and constants
var currentVersionCode = 1 //變量當前的版本號,類型Int可以根據值推斷出來
var currentVersionName : String = "1.0" //顯式標明類型
val APPNAME = "droidyue.com" //常量APPNAME 類型(String)可以根據值推斷出來

//methods
fun main(args: Array<String>) {
println(args)
}

// class
class MainActivity : AppCompatActivity() {

}

// data class 自動生成getter,setting,hashcode和equals等方法
data class Book(var name: String, val price: Float, var author: String)

//支持默認參數值,減少方法重載
fun Context.showToast(message: String, duration:Int = Toast.LENGTH_LONG) {
Toast.makeText(this, message, duration).show()
}

Kotlin支持類型推斷,沒有Java那樣的啰嗦。
另外用var表示變量,val表示常量更加的簡潔
方法也很簡單,連function都縮寫成了fun,平添了幾分雙關之意。
類的繼承和實現很簡單,使用:即可
Kotlin每個句子都不需要加分號(;)
空指針安全

空指針(NullPointerException或NPE)是我們使用Java開發程序中最常見的崩潰了。因為在Java中我們不得不寫很多防禦性的代碼,比如這樣

public void test(String string) {
if (string != null) {
char[] chars = string.toCharArray();
if (chars.length > 10) {
System.out.println(((Character)chars[10]).hashCode());
}
}
}
在Kotlin中空指針異常得到了很好的解決。

在類型上的處理,即在類型後面加上?,即表示這個變量或參數以及返回值可以為null,否則不允許為變量參數賦值為null或者返回null
對於一個可能是null的變量或者參數,在調用對象方法或者屬性之前,需要加上?,否則編譯無法通過。
如下面的代碼就是Kotlin實現空指針安全的一個例子,而且相對Java實現而言,簡直是一行代碼搞定的。

fun testNullSafeOperator(string: String?) {
System.out.println(string?.toCharArray()?.getOrNull(10)?.hashCode())
}

testNullSafeOperator(null)
testNullSafeOperator("12345678901")
testNullSafeOperator("123")

//result
null
49
null

關於空指針安全的原理,可以參考這篇文章研究學習Kotlin的一些方法

支持方法擴展

很多時候,Framework提供給我們的API往往都時比較原子的,調用時需要我們進行組合處理,因為就會產生了一些Util類,一個簡單的例子,我們想要更快捷的展示Toast信息,在Java中我們可以這樣做。

public static void longToast(Context context, String message) {
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
}

但是Kotlin的實現卻讓人驚奇,我們只需要重寫擴展方法就可以了,比如這個longToast方法擴展到所有的Context對象中,如果不去追根溯源,可能無法區分是Framework提供的還是自行擴展的。

fun Context.longToast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}
applicationContext.longToast("hello world")
4
註意:Kotlin的方法擴展並不是真正修改了對應的類文件,而是在編譯器和IDE方面做得處理。使我們看起來像是擴展了方法。

Lambda, 高階函數,Streams API, 函數式編程支持

所謂的Lambda表達式是匿名函數,這使得我們的代碼會更加的簡單。比如下面的代碼就是lambda的應用。

findViewById(R.id.content).setOnClickListener {
Log.d("MainActivity", "$it was clicked")
}

所謂的高階函數就是

可以接受函數作為參數
也可以返回函數作為結果
舉一個接受函數作為參數的例子。在Android開發中,我們經常使用SharedPreference來存儲數據,如果忘記調用apply或者commit則數據修改不能應用。利用Kotlin中的高階函數的功能,我們能更好的解決這個問題

fun SharedPreferences.editor(f: (SharedPreferences.Editor) -> Unit) {
val editor = edit()
f(editor)
editor.apply()
}

//實際調用
PreferenceManager.getDefaultSharedPreferences(this).editor {
it.putBoolean("installed", true)
}

當然這上面的例子中我們也同時使用了方法擴展這個特性。

Kotlin支持了Streams API和方法引用,這樣函數式編程更加方便。比如下面的代碼就是我們結合Jsoup,來抓取某個proxy網站的數據,代碼更加簡單,實現起來也快速。

fun parse(url: String): Unit {
Jsoup.parse(URL(url), PARSE_URL_TIMEOUT).getElementsByClass("table table-sm")
.first().children()
.filter { "tbody".equals(it.tagName().toLowerCase()) }
.flatMap(Element::children).forEach {
trElement ->
ProxyItem().apply {
trElement.children().forEachIndexed { index, element ->
when (index) {
0 -> {
host = element.text().split(":")[0]
port = element.text().split(":")[1].toInt()
}
1 -> protocol = element.text()
5 -> country = element.text()
}
}
}.let(::println)
}
}

字符串模板

無論是Java還是Android開發,我們都會用到字符串拼接,比如進行日誌輸出等等。在Kotlin中,字符串模板是支持的,我們可以很輕松的完成一個字符串數組的組成

val book = Book("Thinking In Java", 59.0f, "Unknown")
val extraValue = "extra"
Log.d("MainActivity", "book.name = ${book.name}; book.price=${book.price};extraValue=$extraValue")

註意:關於字符串拼接可以參考這篇文章Java細節:字符串的拼接

與Java交互性好

Kotlin和Java都屬於基於JVM的編程語言。Kotlin和Java的交互性很好,可以說是無縫連接。這表現在

Kotlin可以自由的引用Java的代碼,反之亦然。
Kotlin可以現有的全部的Java框架和庫
Java文件可以很輕松的借助IntelliJ的插件轉成kotlin
Kotlin應用廣泛

Kotlin對Android應用開發支持廣泛,諸多工具,比如kotterknife(ButterKnife Kotlin版),RxKotlin,Anko等等,當然還有已經存在的很多Java的庫都是可以使用的。

除此之外,Kotlin也可以編譯成JavaScript。最近使用Kotlin寫了一段抓取proxy的代碼,實現起來非常快捷。甚至比純javascript實現起來要快很多。

fun handle(): Unit {
window.onload = {
document.getElementsByClassName("table table-sm").asList().first()
.children.asList().filter { "TBODY".equals(it.tagName.toUpperCase()) }
.flatMap { it.children.asList() }.forEach {
var proxyItem = ProxyItem()
it.children.asList().forEachIndexed { index, element ->
when (index) {
0 -> {
proxyItem.host = element.trimedTextContent()?.split(":")?.get(0) ?: ""
proxyItem.port = element.trimedTextContent()?.split(":")?.get(1)?.trim()?.toInt() ?: -1
}
1 -> proxyItem.protocol = element.trimedTextContent() ?: ""
5 -> proxyItem.country = element.trimedTextContent() ?: ""
}
}.run {
console.info("proxyItem $proxyItem")
}

}
}
}

關於性能

Kotlin的執行效率和Java代碼的執行效率理論上一致的。有時候Kotlin可能會顯得高一些,比如Kotlin提供了方法的inline設置,可以設置某些高頻方法進行inline操作,減少了運行時的進棧出棧和保存狀態的開銷。

讀到這裏,是不是想要嘗試一下Kotlin呢,它簡潔的語法,匯集諸多特性,高效率實現等等,已經在國外風生水起,國外的Pintereset, Square, Flipboard等公司已經開始應用到生產中。

關於轉向Kotlin

其實,我在做決定之前(當時Kotlin還沒有被欽定)也曾有過考慮,是不是選擇了Kotlin就意味著放棄Java呢,冷靜下來想一想,其實並不是那麽回事,因為Kotlin與Java語法太相近,以及在Kotlin中無時無刻不在和Java相關的東西打交道,所以這點顧慮不是問題的。

對於個人的項目來轉向Kotlin,通常不是很難的選擇,畢竟Kotlin是那麽優秀的語言,相信很多人還是願意嘗試並使用這個事半功倍的語言的。

而比較難抉擇的情況是如果如何讓團隊轉用Kotlin,個人認為團隊難以轉用的原因有很多,比如學習成本,歷史包袱等等。但其實根本原因還是思維方式的問題,歪果仁喜歡用工具來提升開發效率,因為人力成本很高。而國內團隊提高效率的辦法通常是增加成員。好在Flipboard 美國團隊自2015年(可能更早)就引入了Kotlin,因此中國團隊這邊選用Kotlin也更加順水推舟。當然更主要的是目前團隊規模不大,成員一致認可Kotlin的優點。

關於團隊轉用Kotlin的方法,一般比較行得通的辦法是自上而下的推行。這就意味著要麽直接的技術負責人比較開明要麽就是需要有人來不斷推介來影響團隊。

做個比較現實的比擬,Java就像是一趟從我的家鄉保定開往北京西的耗時將近2個小時甚至更長的普通列車,而Kotlin則是那趟僅需40分鐘就能到達的高鐵。通常的人都會選擇高鐵,因為它節省了時間和提高了體驗。這個時間和體驗對應編程中的,我想應該是高效率和高可讀性,可維護性的代碼。

北京激光祛斑:www.6ysh.com


為什麽我要改用Kotlin