雖然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官方文檔,並且多動手寫一些代碼。