使用 Kotlin 進行 Android 開發
What is Kotlin
Kotlin,原意是在俄羅斯的一個 小島 ,JetBrain在2011年推出了以這個來命名的一個運行在JVM上的語言, 看上去有點類似C#和Scala的結合,并且同為靜態類型,作為一門JVM上的語言,可以輕松兼容Java,并且整個語言設計的非常輕量。目前的版本為0.12.200,尚未發布正式版。
Kotlin的下載和配置在其 官網 上有,在這里就不再贅述了,值得一提的是,作為JetBrains家出品的語言,自家的IDEA當然全力支持!
基本語法介紹
Kotlin的語法非常簡潔,熟悉Java或者Scala的人都可以快速上手:
函數聲明:
fun foo(va: Int): Int { return 1 }
也可以單行聲明:
fun foo(va: Int): Int = 1
lambda當然也是支持的:
var c = {foo: Int -> println(foo)}
Kotlin中的函數是一等對象,自然支持高階函數:
var c = {foo: Int -> println(foo)} fun fooTest(func: (Int)->()) = println("I'm Groot") fooTest(c)
類與接口
類可以這樣進行聲明:
class Bar(var b: Int): Foo() { var c = 1 init { println("class initializer") } constructor(): this(1) { println("secondary constructor") } }
Bar類在這里繼承了Foo類,Bar類有兩個構造函數,直接在Bar類頭的是primary constructor,另外一個構造函數使用constructor關鍵字定義,注意必須要先調用primary constructor,另外,init標明的是class initializer,每個構造函數都會首先調用class initializer里面的代碼,再調用構造函數
Inner class:
class Outer { class Inner { } }
Kotlin同樣支持嵌套的內部類,不過和Java不一樣的是,Kotlin的內部類不會默認包含一個指向外部類對象的引用,也就是說,Kotlin中所有的內部類默認就是 靜態 的,這樣可以減少很多內存泄露的問題。另外,如果需要在內部類中引用外部類對象,可以在Inner類的聲明前加上inner關鍵字,然后在Inner類中使用標記的this:this@Outer來指向外部類對象
Singleton:
object Single { var c = 1 fun foo() = println("foo") }
Kotlin中使用object關鍵字聲明一個singleton對象,后面這里的方法就可以直接使用Single.foo()來調用了
Interface:
interface Interface { fun foo() { println(1) } fun bar() }
Kotlin中的interface,跟其他語言的trait非常像,而且也可以帶有默認的實現方法,并且不允許通過屬性來維護狀態。事實上,在上個版本中,interface的原來名稱是trait,而在M12現在這個版本中又改成了interface而已
Null safe and Smart type cast
Null safe:
在Kotlin中,嚴格區分了nullable和非nullable對象,甚至在編譯期解決了不少潛在的空指針問題:
我們先來看下普通的變量聲明
var c: String = "12123"
這里聲明了一個String對象,其值為"12123",我們可以正常的使用這個對象的成員方法:c.length()
但是,如果在初始化的時候,變量c為空的話,這樣聲明就是錯誤的,會編譯不過:
var c: String = null
正確的聲明應該是這樣:
var c: String? = null
這里在String后面加多了一個問號,表明這里是一個 Nullable 的對象,說明這個變量在使用的過程中 可能為空 ,而且,在調用這個變量的成員的時候,必須要使用這種語法:c?.length(),在調用的時候添加了一個問號,表明,如果c為空的時候,length()這個方法就不會調用。coffe-script也有類似的,這種語法糖減少了很多平時用到的Null-checked,簡化了代碼,而且從編譯器開始介入null-checked,大大減少了潛在的NullPointerException,而事實上,null的確也是一個 billion dollar mistake
常年進行如此的調用語法常常會很惱人,因此在你進行顯式的Null-checked的時候,Kotlin的編譯器會認為后續的調用已經無需進行Null-checked,可以直接調用了:
if (c != null) { c.length() }
Smart type cast
在Kotlin中,進行強制類型轉換可以使用as關鍵字,但有可能會拋出異常,因此,Kotlin引入了smart type cast:
if (c is String) { c.length() }
在上面的例子中,如果c是一個String對象,則在if塊中,可以直接使用String的方法,編譯器會智能的幫你識別出c在if-blcok里面是一個String對象
Pattern Matching
Kotlin在一定程度上支持了一些FP的特性,包括強大的Pattern Matching,在Kotlin中可以使用when關鍵字:
var x = 3 when (x) { 1 -> print("x == 1") 2 -> print("x == 2") in 1..10 -> print("x is in the range") !in 10..20 -> print("x is outside the range") is Int -> println("is int") else -> { // Note the block print("x is neither 1 nor 2") } }
Function Extension
在Java中我們經常需要給系統的類添加一些實用的方法,但苦于不能直接擴展,于是就有了各種的xxxUtils類,導致代碼非常惡心,但是在Kotlin中,我們可以直接擴展庫里面類的方法,通過function extension:
fun String.fucker() { println("a fucker") }
上面給String類添加了一個fucker方法,我們可以直接使用:
"123123".fucker()
這大大的減少了我們寫xxxUtils類的必要性
配置使用Kotlin進行Android開發
使用Kotlin開發Android app的配置非常簡單,按照 官方給出的配置即可 ,直接在Gradle的配置文件build.gradle中添加一個依賴:
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
然后添加Kotlin插件的使用:
apply plugin: 'kotlin-android'
進行一次Gradle Sync之后,就可以直接在項目使用Kotlin編寫代碼了,另外,如果安裝了Intellij的Kotlin插件,可以選擇Tools->Kotlin->Configure Kotlin in Project,就可以自動進行上述的配置,一步到位
我寫了一個簡單的Demo app放到了Github上,有興趣可以看下使用Kotlin開發android app具體是怎樣的: Demo地址
對于dex方法數目的影響
dex有個65535方法數的限制,這對Android開發造成了很大的影響,在使用Kotlin進行android app開發的時候,需要將Kotlin的標準庫打包進入apk中,這意味著如果標準庫過大,對分包會造成很大的限制(因為這必須得打包在主dex中),所幸的是,Kotlin的哲學是“Java中有的,就盡量復用,不再自行創造一套”,使得整個Kotlin的標準庫非常小,我們可以簡單將Kotlin的標準庫和其他比較常用庫進行一下對比:
包名 | android-support-v13.jar | android-support-v4.jar | android-support-v7-appcompat.jar | guava-18.0.jar | scala-library-2.12.0-M1.jar | kotlin-stdlib-0.12.213.jar |
方法數 | 8219 | 8118 | 4624 | 14833 | 51248 | 7228 |
可以看出來Kotlin的標準庫相當小,只有7000多個方法,比support-v13和support-v4還小,這體現了Kotlin的設計哲學之一:"100% interoperable with Java",基本上Java已經有的,Kotlin會盡量復用。而對比來看,同樣是JVM上的語言,我們也可以選擇使用Scala來進行Android開發,但Scala標準庫有5萬多個方法,全部打包進主dex中,很容易就導致app爆主dex了。所以綜合來看,輕量形的Kotlin還是相當適合進行Android開發的。
Project Anko
Anko 是JetBrains推出的一個簡化Android開發的庫,同樣由Kotlin來編寫,主要的革命在于,聲明UI的方式,完全拋棄了xml的使用,使用Anko,聲明UI是這樣做的:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val customStyle = { v: Any -> when (v) { is Button -> v.textSize = 26f is EditText -> v.textSize = 24f } } verticalLayout { padding = dip(34) imageView(android.R.drawable.ic_menu_manage).layoutParams { margin = dip(16) gravity = Gravity.CENTER } val name = editText { hintResource = R.string.name } val password = editText { hintResource = R.string.password inputType = TYPE_CLASS_TEXT or TYPE_TEXT_VARIATION_PASSWORD } button("Log in") { onClick { tryLogin(name.text, password.text) } } }.style(customStyle) }
你沒看錯,的確是在Activity類的onCreate方法中直接聲明UI的布局。
Anko看起來像是使用了一種類似DSL的方式聲明了界面的UI,這里主要是使用了Kotlin的其中兩個特性:
- Function Extension,Anko擴展了Activity類,提供了額外的方法和屬性
- Kotlin在調用函數的時候,如果最后一個參數為函數的話,則可以直接使用Lambda,并省略括號
因此這里聲明布局的方式,其實全是Kotlin的原生代碼,鵝妹子嚶!這樣做有顯然的好處:
- 由于實際上全是由代碼來布局,省去了解析xml的時間
- xml本身有許多缺點,例如不可重用,非類型安全等,使用代碼布局的話,我們可以很容易的就解決這個問題了
Other References:
這里列出一些國外的關于Kotlin的介紹文章:
- Using Project Kotlin for Android Square的JakeWharton曾經考察過幾種不同的語言來進行Android開發,最后還是認為Kotlin比較優秀
- Kotlin, the Swift of Android 這篇文章把Kotlin比喻為Android上的swift,雖然比較老,但是也可以看做Kotlin的介紹性文章