官方文檔講了如何1步步安裝Kotlin插件,并使用插件在Android項目中自動修改Gradle文件來添加對Kotlin的支持。我不建議大家這么做,由于這樣自動完成的結果可能其實不完善,即便修改后的Gradle文件工作正常,也會打亂Android項目中Gradle文件的1般情勢。
說實話,我1直不喜歡有些Android Studio插件直接修改Android build文件,由于常常弄得很亂,我又得1點1點清算直到符合我的風格。Gradle構建文件也是源代碼,而這些插件其實不善于修改已存在的代碼。所以如果你也和我1樣有些挑剔,那就多花1分鐘和我1起手動配置。
下面我們要分4步完成Kotlin的配置。
新建1個Android項目。
修改Gradle代碼來添加Kotlin Gradle插件與標準庫。
在IntelliJ或Android Studio中添加Kotlin插件。
將Java類文件轉換成Kotlin。
首先,直接以默許方式新建1個Android項目,此時應當自帶1個Activity。以后,要在兩個build.gradle文件中添加5行重要代碼,我都在其后添加了注釋。下面讓我們先修改最高層的build.gradle腳本,添加兩行代碼。
這樣就會在項目構建時添加Kotlin Gradle插件。請注意上面在ext.kotlin_version中標注的kotlin版本字符串,我們1會還要在app模塊的compile dependencies中用到它,而且兩個地方版本必須符合。你最好使用官方文檔中最新版本。
然后,在app模塊自己的build.gradle文件中緊隨Android plugin添加kotlin-android plugin。這樣全部項目就整合了Kotlin,在build項目時會編譯Kotlin文件,這樣最后所有的類文件都會打包在1個app中。
慣例上,Kotlin文件寄存在src/main/kotlin路徑中,但也能夠把他們和Java文件1起放在/src/main/java路徑中。這里我們還是依照慣例,并在Gradle中標注1個新的Kotlin源路徑。
不要忘了新建這個路徑,1會就要用到了。最后需要添加1個Kotlin依賴,直接使用build.gradle中的kotlin版本變量。
不過這個包有多大呢?好問題!每當我們添加新的依賴時,都應當弄清楚這個包有多大。不過對這個問題,我會在后面的文章中回答。
這就是Kotlin Gradle插件,走完這些步驟后,就能夠在項目中運行Kotlin代碼了。不過你還需要添加IDE對Kotlin的支持,所以如果你還沒有安裝IntelliJ或Android Studio的Kotlin插件,那就趕快安裝。安裝Kotlin插件就像安裝其他任何插件1樣,可以在Preferences->Plugins->Insall JetBrains plugin下找到。安裝后要重啟IDE,做完這1步后,準備工作就完成了。我發現IDE對Kotlin的支持乃至和Java語言1樣好。這也能夠理解,畢竟IDE和Kotlin都是JetBrains開發的嘛。
IDE插件有1個很有趣的功能就是將Java文件直接轉成Kotlin。這個插件可以很智能地將Java語言風格轉換成Kotin風格并保持運行兼容。如果你創建了1個Android項目,那就找到自動生成的MainActivity,在左側的項目結構當選中,并觸發IDE的action "Convert Java File to Kotlin File"。你可以按下快捷鍵Command+Shift+A(OSX)來選擇action。這個插件乃至有專門針對這個action的快捷鍵Option+Shift+Command+K(OSX)。其實官方其實不建議直接轉換Java文件,但直到現在我還沒遇到過甚么問題。
如果你按我說的操作轉換了Java文件,就會在本來.java文件的地方找到1個.kt文件。
你可以看到現在MainActivity在右下角有1個K標志(這里隱藏了.kt擴大名)。由于我們剛剛專門為Kotlin配置了1個路徑,我們把Kotlin文件拖進kotlin文件夾中。注意要保存kotlin文件中類的包名,不然項目沒法運行。
如果你想在項目中只用Kotlin,那可以干脆刪除掉Java文件夾,把所有的Kotlin文件放在kotlin路徑中。以后,項目結構就像下面這樣。
重新的Activity中你可以大概知道Kotlin長甚么樣。我下面說幾點Kotlin與Java很不1樣的地方:
在Kotlin中你見不到"new"關鍵字。
把類名當作方法并傳入參數就能夠直接構造對象。
數據類型關鍵字被val(final)與var(variable)取代,Kotlin可以自己判斷數據是甚么類型。
剛才我們1步1步在Android項目中添加了Kotlin支持,現在我們要開始通過代碼直觀感受Kotlin的語言特點和它能如何簡化Android開發。
當我第1次接觸Kotlin并了解其語言特點功能時,有1點讓我感觸很深,那就是type-safe builders。它讓你以陳說式語言風格來創建對象,其語法很類似Gradle,但Gradle和Groovy代碼是動態編寫的,而kotlin是靜態編寫的,所以編譯器可以在屬性的值不合法時告知你。
type-safe builders的1個典型用法就是構建嵌套式數據結構,比如XML。在Android中有很多XML文件,如layouts和views。如果Kotlin可以以程序的方式動態編寫XML,它可能很善于處理層級問題。所以我決定嘗試使用Kotlin創建1個動態構建View層級的工具。如果是在Java中做這件事,代碼量之大可以想象。
注意:在后面的代碼中我會常常使用lambda,所以在繼續瀏覽之前,確保你了解lambda的基本情勢。簡而言之,lambda是要作為參數被傳入某方法或賦值給某變量的匿名方法的簡化表現情勢。
type-safe builders可行還要歸功于Kotlin的1個特點功能:lambda with receiver。下面我們要看1個能真正有用的案例。kotlin可以在類的外面定義方法,我在這里就是這么干的。還要注意變量的名字是在類型之前的,這1點與Java語言正相反。
(點擊放大圖象)
簡便起見,我將上面的方法命名為v,在以后的系列文章中我還會使用到這個方法。這個方法是這么調用的:
這段代碼和填充下面這段XML是等效的。
OK,如果這是你第1次看Kotlin代碼,相信有很多需要翻譯的。下面解釋1下剛才代碼中觸及的幾個語法。
<reified TV : View>
reify的意思是具體化。而作為Kotlin的1個方法泛型關鍵字,它代表你可以在方法體內訪問泛型指定的JVM類對象。這段代碼的意思就是v方法要使用命名為TV(意為Type of View,即View種類)的reified泛型,它指定類必須為View或其子類。必須之內聯方式聲明這個方法才有效。調用者要給TV指定1個具體的類型。
init: TV.() -> Unit
v方法有兩個參數,1個是Context,1個是lambda風格的init。init在這里很特殊,由于它是1種lambda with receiver類型的方法援用。lambda with receiver是1個要求特定類型的對象的代碼塊,這里要求的對象在lambda代碼中通過this關鍵字援用。在這里receiver對象就是reified泛型TV。
在我們這個例子中,v要創建1個類型為TV的對象,需要調用者告知它如何創建。這個新創建的TV類型對象會成為給定的lambda的receiver,labmda通過view.init()在v中被調用,以便對view進行操作。“-> Unit”意思是lambda返回Unit類型,就如Java中的Void1樣,即甚么也不返回。
總結1下這里的lambda with receiver:
v聲明了1個名叫init的參數,它是TV類型的lambda with receiver。
v創建并初始化1個TV對象,并在其上調用lambda來初始化它。
lambda在自己代碼塊中通過this關鍵字援用TV對象。
TV::class.java
通過TV::class.java表達你可以援用reified泛型TV的Class對象。這類表達在Kotlin中專門針對reified泛型,與Java相比,可以大量減少代碼。
到這里,估計你會有1些問題:
為何v有兩個參數,而調用的時候似乎只給了1個?
這個只是由于Java程序員對Kotlin語法其實不熟習。在Java中,1個方法的所有的參數都要寫在圓括號內,所以當有匿名回調方法時會變得很長。但在Kotlin中,當lambda時最后1個參數時,有1個特殊語法,即lambda出現在緊隨圓括號的大括號中。你可以把所有的代碼都放在圓括號內,但大多數情況下這么寫代碼會顯得更加簡潔,而且可使1組圓括號保持在1行里,更容易于追蹤。另外,這類語法在傳入只有1個方法的匿名內部類比如Runnable時也能夠派上用處。
layoutParams和text是變量嗎?
lambda with receiver的1個語法特點就是當操作this的方法或屬性時this關鍵字可以省略。但在上面的調用例子中layoutParams和text究竟是甚么?其實這些屬性是Kotlin提供的receiver類型(TV)的屬性。由于TextView有setLayoutParams()與setText()等方法,Kotlin會自動辨認這些JavaBean風格的方法并為他們創建屬性,好像它們是類的成員變量1樣。所以這里text = "Hello"等價于this.setText("Hello")。下面是在安裝了Kotlin插件的Android Studio中的截圖,里面展現了自動完成的內容。
如你所見,Kotlin插件的地方text屬性是從TextView(receiver對象)中JavaBean風格的getter/setter方法中派生出的。
構造器究竟是怎樣運作的?就不能new TV(context)嗎?
由于編譯器也不知道在v方法內部TV究竟是甚么類型,我們不能new它的對象。不過我們可以利用reified Class對象(TV::class.java)來獲得唯一1個context參數的構造器。View都有這樣1個構造器,不然我們也寫不出這個程序。我們通過構造器對象來取得TV類型的實例,就如Java中的new關鍵字1樣。想想我們可以通過1個方法適配所有View,而不再針對每一個View單獨寫方法,這1點小麻煩還是很值的。在后續系列文章中我們也會優化這個方法。
這幾行代碼真的很大的簡化了我們的工作。如果你剛剛接觸Kotlin,建議你從頭再消化1遍,畢竟許多概念和Java差別很大。我自己也花了好多時間來理解這些概念。
這些都只是開始,這個方法有很多地方可以完善和升級,使之更加易用。比如我想用用1行代碼構造1全部View層級。所以不要走開,我會在后續文章探索Kotlin還能做甚么。