Kotlin簡記

雖然Anko效率很高,代碼簡潔,清爽直觀,但是目前還有很多坑,主要包括:

1.AS並不支持直接預覽Anko界面,雖然有個Anko DSL Preview插件,但是需要make才能刷新,而且和現在的AS不兼容。

2.如果要在多版本中動態替換外部資源,需要用動態類加載才能實現,無法借用資源apk實現。

3.不方便根據view的id去即時引用view控件(R文件和inflate這時反而愈加靈活)。

另外,Anko還在異步、日誌、Toast、對話框、數據庫等方面提供優化服務,能否採用就看自身需要了。

 

相對Java而言,主要的變化有這麼幾條:

1.沒有“;”

在Kotlin語法裏,代碼行不需要用“;”結尾,什麼都不寫就好

2.重要的“:”

在Java裏,“:”主要在運算符裏出現(for/switch/三元運算符等)。

在Kotlin裏,“:”的地位大大提升了,它的用途非常廣泛,包括:

定義變量類型

var name:String="my name" //變量name爲String類型

定義參數的類型

fun makeTool(id:Int){ //參數id爲Int類型

}

定義函數的返回值

fun getAddr(id:Int):String{ //返回值爲String類型

}

聲明類/接口的繼承

class KotlinActivityUI :AnkoComponent<KotlinActivity>{//繼承AnkoComponent接口

使用Java類

val intent = Intent(this, MainActivity::class.java) //需要用::來使用Java類,注意是兩個“”

3.沒有“new”

Kotlin實例化一個對象時不需要new關鍵字

var list=ArrayList()

4.變量、常量、類型推斷

用var定義變量(像js)

var name:String="my name"

用val定義常量(相當於final)

val TAG:String="ClassName"

上面兩個例子用:String來定義了數據類型,這個是可以省略的,Kotlin支持類型推斷,這兩句話你可以寫成

var name="my name"

val TAG="ClassName"

5.初始化和延遲加載

在Java裏,我們可以定義一個變量,但是並不賦值(int和boolean會有默認值)

但是Kotlin裏必須爲變量賦值,如果只寫一個變量,卻不賦值,像下面這樣:

var name

編譯器會報錯,提示你未初始化,你必須賦值爲0或者null,或者別的什麼值。

不過,我們有時候就是不能在定義變量時就初始化它,比如在Android中我們經常預定義一個View控件而不初始化,但是直到onCreate或onCreateView時才初始化它。

針對這種情況,Kotlin提供了懶加載lazy機制來解決這個問題,在懶加載機制裏,變量只有在第一次被調用時,纔會初始化,代碼需要這樣寫


在初次調用時初始化變量的lazy機制

lazy只適用於val對象,對於var對象,需要使用lateinit,原理是類似的,只是代碼需要這樣寫


var變量使用lateinit機制

6.空指針安全

在Kotlin裏,可以用“?”表示可以爲空,也可以用“!!”表示不可以爲空。

空指針安全並不是不需要處理空指針,你需要用“?”聲明某個變量是允許空指針的,例如:

var num:Int?=null

聲明允許爲空時,不能使用類型推斷,必須聲明其數據類型

空指針雖然安全了,但對空指針的處理還是要視情況而定,有時候不處理,有時候做數據檢查,有時候還需要拋出異常,這三種情況可以這樣寫:

val v1 =num?.toInt() //不做處理返回 null

val v2 =num?.toInt() ?:0 //判斷爲空時返回0

val v3 =num!!.toInt() //拋出空指針異常(用“!!”表示不能爲空)

更多空指針異常處理,有一篇NullPointException 利器 Kotlin 可選型介紹的比較全面,值得借鑑

7.定義函數

在Kotlin語法裏,定義函數的格式是這樣的

fun 方法名(參數名:類型,參數名:類型...) :返回類型{

}

所以,一般來說,函數是這樣寫的

fun getAddress(id:Int,name:String):String{

    return"got it"

}

由於Kotlin可以對函數的返回值進行類型推斷,所以經常用“=”代替返回類型和“return”關鍵字,上面這段代碼也可以寫成

fun getAddress(id:Int,name:String)={ //用“=”代替return,返回String類型則交給類型推斷

     "got it" //return被“=”代替了

}

如果函數內代碼只有一行,我們甚至可以去掉{}

fun getAddress(id:Int,name:String)="got it" //去掉了{}

}

函數也允許空指針安全,在返回類型後面增加“?”即可

fun getAddress(id:Int,name:String) :String?="got it"

有時候,函數的返回類型是個Unit,這其實就是Java中的void,表示沒有返回

fun addAddress(id:Int,name:String):Unit{ //相當於java的void

}

不過,在函數無返回時,一般不寫Unit

fun addAddress(id:Int,name:String){ //相當於java的void

}

8.用is取代了instance of

代碼很簡單

if(obj is String)...

9.in、區間和集合

Kotlin裏有區間的概念,例如1..5表示的就是1-5的整數區間

可以用in判斷數字能否在某個區間

if(x in 1..5){ ...//檢查x數值能否在1到5區間

可以用in判斷集合中能否存在某個元素

if(name in list){...//檢查list中能否有某個元素(比Java簡潔多了)

可以用in遍歷整個集合

for(i in 1..5){ ...//遍歷1到5

for(item in list){...//遍歷list中的每個元素(相當於Java的for(String item : list))

另外,in在遍歷集合時功能相當強大:

在遍歷集合時,可以從第N項開始遍歷

for(i in 3..list.size-2){...相當於for (int i = 3; i <= list.size()-2; i++)

可以倒序遍歷

for(i in list.size downTo 0) {...相當於for (int i = list.size(); i >= 0; i--)

可以反轉列表

for(i in (1..5).reversed())

可以指定步長

for(i in 1.0..2.0 step 0.3) //步長0.3

Kotlin裏的集合還都自帶foreach函數

list.forEach {...

10.用when取代了switch

switch在Java裏一直不怎麼給力,在稍老一些的版本里,甚至不支持String

Kotlin乾脆用強大的when取代了switch,具體用法如下


when的用法

代碼中的參數類型Any,相當於Java中的Obejct,是Kotlin中所有類的基類,至於object關鍵字,在Kotlin中另有用處...

11.字符串模板

在Java裏使用字符串模板沒有難度,但是可讀性較差,代碼一般是

MessageFormat.format("{0}xivehribuher{1}xhvihuehewogweg",para0,para2);

在字符串較長時,你就很難讀出字符串想表達什麼

在kotlin裏,字符串模板可讀性更好

"${para0}xivehribuher${para1}xhvihuehewogweg"

12.數據類

數據類是Kotlin相對Java的一項重大改進,我們在Java裏定義一個數據Model時,要做的事情有很多,例如需要定義getter/setter(雖然有插件代寫),需要自己寫equals(),hashCode(),copy()等函數(部分需要手寫)

但是在Kotlin裏,你只需要用data修飾class的一行代碼

data class Client(var id:Long,var name:String,var birth:Int,var addr:String)

Kotlin會自動幫你實現前面說的那些特性。

數據模型裏經常需要一些靜態屬性或方法,Kotlin可以在數據類裏添加一個companion object(伴隨對象),讓這個類的所有對象共享這個伴隨對象(object在Kotlin中用來表示單例,Kotlin用Any來表示所有類的基類)


添加companion obejct

13.單例模式

單例是很常見的一種設計模式,Kotlin乾脆從語言級別提供單例,關鍵字爲object,如果你在擴展了Kotlin的IDE裏輸入singleton,IDE也會自動幫你生成一個伴隨對象,也就是一個單例


單例類

如果一個類需要被定義爲class,又想做成單例,就需要用上一節中提到的companion object

例如,如果我們用IDE新建一個blankFragment,IDE會自動幫我們寫出下面的代碼,這本來是爲了解決Fragment初始化時傳值的問題,我們注意到她已經使用了companion object單例


自動生成的代碼

如果我們修改一下newInstance這個函數


改成單例

那麼,我們用

BlankFragment.newInstance()

就可以調用這個fragment的單例了

14.爲已存在的類擴展方法和屬性

爲了滿足開放封閉原則,類是允許擴展,同時嚴禁修改的,但是實現擴展並不輕鬆,在Java裏,我們需要先再造一個新的類,在新類裏繼承或者引用舊類,然後才能在新類裏擴展方法和屬性,實際上Java裏層層嵌套的類也非常多。

在Kotlin裏,這就簡潔優雅地多,她允許直接在一箇舊的類上做擴展,即便這是一個final類。

例如,Android中常見的Toast,參數較多,寫起來也相對繁瑣,我們一般是新建一個Util類去做一個相對簡單的函數,比如叫做showLongToast什麼的,我們不會想在Activity或Fragment中擴展這個函數,因爲太麻煩,我們需要繼承Activity做一個比如叫ToastActivity的類,在裏面擴展showLongToast函數,然後把業務Activity改爲繼承這個ToastActivity...

在Kotlin裏,我們只需要這樣寫


擴展函數

就完成了Activity類的函數擴展,我們可以在Activity及其子類裏隨意調用了


調用擴展函數

需要注意的是,你無法用擴展去覆蓋已存在的方法,例如,Activity裏已經有一個onBackPressed方法,那麼你再擴展一個Activity.onBackPressed方法是無用的,當你調用Activity().onBackPressed()時,它只會指向Activity本身的那個onBackPressed方法。

我們還可以用類似的方式去擴展屬性


擴展屬性

不過,Kotlin的擴展其實是僞裝的,我們並沒有真正給Activity類擴展出新的函數或屬性,你在A類裏爲Activity擴展了函數,換到B類裏,你就找不到這個函數了。

這是因爲,Kotlin爲類擴展函數時,並沒有真的去修改對應的類文件,只是藉助IDE和編譯器,使他看起來像擴展而已。

所以,如果類的某些函數只在特殊場景下使用,可以使用靈活簡潔的擴展函數來實現。

但是,如果想爲類永久性地添加某些新的特性,還是要利用繼承或者裝飾模式(decorator)。

不過,Kotlin裏對於類的家族定義和Java有所不同,我們來看一下

15.類的家族結構

Kotlin關於類的家族結構的設計,和Java基本相似,但是略有不同:

Object:取消,在Java裏Object是所有類的基類,但在Kotlin裏,基類改成了Any

Any:新增,Kotlin裏所有類的基類

object:新增,Kotlin是區分大小寫的,object是Kotlin中的單例類

new:取消,Kotlin不需要new關鍵字

private: 仍然表示私有

protected: 類似private,在子類中也可見

internal: 模塊內可見

inner:內部類

public: 仍然表示共有,但是Kotlin的內部類和參數默認爲public

abstract:仍然表示抽象類

interface:仍然表示接口

final:取消,Kotlin的繼承和Java不同,Java的類默認可繼承,只有final修飾的類不能繼承;Kotlin的類默認不能繼承,只有爲open修飾的類能繼承

open:新增,作用見上一條

static:取消!Java用static去共享同一塊內存空間,這是一個非常實用的設計,不過Kotlin移除了static,用伴隨對象(前面提到過的compaion object)的概念替換了static,伴隨對象其實是個單例的實體,所以伴隨對象比static愈加靈活一些,能去繼承和擴展。

繼承:在Kotlin裏,繼承關係統一用“:”,不需要向java那樣區分implement和extend,在繼承多個類/接口時,中間用“,”區分即可,另外,在繼承類時,類後面要跟()。所以在Kotlin裏,繼承類和接口的代碼一般是這樣的:

class BaseClass : Activity(), IBinder{ //示例

16.構造函數

在Java裏,類的構造函數是這樣的

pulic 類名作爲函數名 (參數) {...}

Java裏有時會重載多個構造函數,這些構造函數都是並列的

在Kotlin裏,類也可以有多個構造函數(constructor),但是分成了1個主構造函數和N個二級構造函數,二級構造函數必須直接或間接代理主構造函數,也就是說,在Kotlin裏,主構造函數有核心地位

主構造函數一般直接寫在類名後面,像這麼寫

class ClientInfo(id:Long,name:String,addr:String){

這其實是個縮寫,完全版本應該是

class ClientInfo constructor(id:Long,name:String,addr:String){

主構造函數的這個結構,基本決定了,在這個主構造函數裏,沒法寫初始化代碼...

而二級構造函數必須代理主構造函數,寫出來的效果是這樣的


二級構造函數

17.初始化模塊init

上一節提到過,主構造函數裏不能寫代碼,這就很麻煩了,不過還好,Kotlin提供了初始化模塊,基本上就是用init修飾符修飾一個{},在類初始化時執行這段兒代碼,代碼像這樣寫就行


初始化模塊

18.其他

Kotlin還有很多其他的語言特性,本文主要是爲了建立對Kotlin的大概印象,更多細節就不再列舉了,建議仔細閱讀Kotlin官方文檔,並且多動手寫一些代碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章