Kotlin, Android的Swift
蘋果已經用Swift代替Objective-C,一種古老的語言,來進行iOS的開發了。明顯Android開發也有這個趨勢。
雖然現在已經可以選擇Scala或者Groovy等基於JVM的語言開發Android應用來嘗嘗鮮,但是弊端卻顯而易見。
要引入一個全新的開發語言,那么就意味着需要引入這個語言的全部的運行時。這簡直就是噩夢。因為這會給
極大的增加應用包的大小,還不說65535方法問題。小的應用還可以,但是這不是一個合適的替代語言應該有的問題。
Kotlin
介紹一下Kotlin--一個基於JVM的語言。由JetBrains(他們開發了IntelliJ IDEA的一個系列和Android Studio)
開發,並由聖彼得堡附近的一個小島命名,負責這個項目的開發團隊就在這個小島辦公。在2011年為外界
所致,並在幾年后的第二個里程碑(M2)發布了對Android的支持(翻譯的時候已經發布了1.0正式版)。
特性
這個新的語言有什么特色呢,你可能會問。有很多!不過本文會集中講述Java應該有而一直沒有的上面。
命名和可選參數
命名參數是一個很簡單的語言特性,但是卻讓代碼更加易讀,尤其是那些有很多參數的函數簽名。
看下面的例子:
void circle(int x, int y, int rad, int stroke) {
...
}
在java調用的時候看起來是這個樣子的:
circle(15, 40, 20, 1);
要多次查看函數簽名才能知道每個參數的具體作用。在Kotlin里,定義是這樣的:
fun circle(x: Int, y: Int, rad: Int, stroke: Int) {
...
}
調用就更好了:
circle(15, 40, rad = 20, stroke = 1)
假設我們現在需要把stroke
參數設定為可選的,在Java里你只能overload出另一個方法,這個方法
少一個參數,然后在里面調用第一個方法:
public void circle(int x, int y, int rad, int stroke) {
...
}
public void circle(int x, int y, int rad) {
circle(x, y, rad, 1);
}
但是在Kotlin里,一切變得簡單:
fun circle(x: Int, y: Int, rad: Int, stroke: Int = 1) {
...
}
不是什么大的功能改進,但是Java始終沒有。
Lambda表達式
最近函數式編程日漸流行。Kotlin也支持lambda表達式。
我們先來看一個簡單的。假設你有一個整數列,而且要刪除全部的奇數。
val numbers = listOf(1, 2, 3, 4, 5)
println(numbers.filter { it % 2 != 0 })
// 輸出:[1, 3, 5]
這個函數把list的元素類型為輸入參數類型,這里是integer。並輸出一個boolean型的結果。如果我們把
filter的lambda表達式轉化為一個明顯的變量寫出來的就更容易理解了:
val predicate: (Int) -> Boolean = { it -> it % 2 != 0 }
numbers.filter(predicate)
這一語法和其他的而支持lambda表達式的語言的語法相差不大,所以不多做解釋。
Null和類型安全
總是要檢查變量是否為null也是Java里一個很煩人的事。這個問題在Kotlin里被部分解決。
如果你用傳統的方法定義一個變量,那么編譯器是不允許其值為null的。
var text1: String = "something" // 木有問題
var text2: String = null // 無法編譯
那么定義的變量的值需要為null怎么辦呢?你要專門定義一個值可為null的變量。值可為null的變量,用變量
類型后面的問號來明確指定。
var text2: String? = null // 木有問題
調用的時候也需要問號。
text2?.replace(" ", "-")
這句的意思是:如果text2
為空,那么replace()
方法會被忽略並且不會有NullPointerException
異常拋出。
如果你接手了一個可選類型(optional type)的變量,你可以百分之百的確認變量的值不會為null(這在Adnroid的API里
非常多見,雖然方法定義返回值為空可空類型,但是從來不會返回null),然后可以強制作為非空調用方法。
text2!!.replace(" ", "-")
這個時候,如果text2
為null的話就會拋出NulPointerException
異常。所以小心使用。
類型安全的另一個意義是類型檢測。假設你又一個Context
的實例並且需要檢測這個實例是否為Activity
類型,然后調用
一些activity才有的方法。
val context = getContext()
if (context is Activity) {
context.finish()
}
類型檢測之后你就可以把context作為一個activity類型的實例來使用了,不需要強制類型轉換。和Java的
instanceOf
方法一樣,is
也是null安全的。即使getContext()
返回一個null,上面的代碼也
不會崩潰。
數據對象(Data object)
在寫數據對象的時候,很多你需要手動實現的,比如:toString()
, 'hashCode()以及
equals()`。即使
現在很多的IDE已經減輕了一部分工作量,但是添加了新的成員后再更新這些實現也是非常的麻煩。
Kotlin里不用再為這些操心了。你需要做的只是在類定義的前面加一個data
的修飾,上面的方法就已經隱式的生成了。
data class Island(val name: String? = null)
如果你初始化上面的類,會自動生成一個人類可以讀懂的toString()
方法。
val island = Island("Kotlin")
println(island.toString()) // 輸出:Island(name=Kotlin)
同理,如果我們用同一個名字創建另外一個實例,這個實例會equal
之前創建的實例。並且他們的hashCode()
值也一樣。
val island = Island("Kotlin")
val island2 = Island("Kotlin")
assert(island.equals(island2))
assert(island.hashCode() == island2.hashCode())
如果你要定制以上的方法,那么,當然,你還是要手寫的。
單例
單例是一個非常常用的方法。Kotlin省去了創建單例的時候需要的靜態getInstance()
方法和私有的構造函數。
Kotlin使用object
聲明。
object ApiConfig {
val baseUrl: String = "https://github.com"
}
object
聲明也可以用來創建靜態方法。
open class MyFragment : Fragment() {
companion object {
fun newInstance(): MyFragment = MyFragment()
}
}
上面的方法可以這樣調用val fragment = MyFragment.newInstance()
,就和Java的靜態方法一樣。
接口
雖然Kotlin不支持多繼承, 但是還是支持接口的。只不過這個接口和Trait
比較接近,可以在接口中包括默認實現。
interface SessionCloseable {
fun closeSession() {
Log.d(SessionCloseable::class.java.simpleName, "Closing...")
}
}
以上接口定義了一個可以關閉session的方法,下面我們在一個activity里實現這個方法。
class KotlinActivity : Activity(), SessionCloseable {
override fun onStop() {
super.onStop()
closeSession()
}
}
有一點需要注意的是Kotlin里沒有extend和implement的區分,什么時候都是逗號分隔。
擴展(extension)
最后我們看一個例子:你可以給一個已經存在的類添加方法。
比如你要創建一個方法,這個方法可以把字符串的空白替換為下划線。在Java里你需要創建一個Utility的方法,以
原始字符串為參數。
public class StringUtils {
public static String encodeString(String str) {
return str.replaceAll(" ", "_");
}
}
在Kotlin里,你可以創建一個擴展方法(extension function),即使原類是final
的。
fun String.encodeSpaces(): String {
return this.replace(" ", "_")
}
最后
為了公平起見,以上說到的大部分特性在Java8里都已經有了。但是這些什么時候可以在Android里用到可能
就遙遙無期了(其實這個問題已經有人在2014的Google I/O里問過了,但是根據Xavier Ducrohet的說法近期沒有這樣的計划)。
還好現在有Kotlin了。
另外,這個語言已經非常的成熟。唯一的問題就是文檔經常補全而且還經常是過期的,不過因為語法和同時JVM語言的Scala多少
有些接近,多少可以猜出個大概。所以,這只是一個很小的瑕疵。
最后的最后,Kotlin將會在我(作者)后續的Android開發中盡可能的使用。顯然,對已有的代碼做轉化不太明智。
而且Kotlin可以和Java的代碼無縫互操作,這樣不是什么太大的問題。Kotlin很值得一試!