至今都不敢相信蚣录,我有一天會寫kotlin的文章,雖然Kotlin在Google I/O中的推出并表示也作為安卓開發(fā)的官方語言纵刘,Kotlin的發(fā)展也是越來越迅猛土榴,但Java仍然是很多開發(fā)者的首選語言,之前習(xí)也慣了java開發(fā)儡炼,Kotlin這個詞僅存在和朋友的聊天記錄中妓湘。
但是公司現(xiàn)在的項目都是kotlin開發(fā),項目開發(fā)過程中和其他時間也學(xué)習(xí)了一段時間乌询,發(fā)現(xiàn)還是挺舒服的榜贴,所以在此分享些心得吧。
真正接觸才發(fā)現(xiàn)呢妹田,有挺多優(yōu)點的
1.語法簡便
Kotlin的語法簡單唬党,Java語言有嚴(yán)格的數(shù)據(jù)類型,轉(zhuǎn)換類型就會很繁瑣鬼佣,同時Kotlin也不用像Java那樣中不斷的非空判斷驶拱,沒有語言基礎(chǔ)的學(xué)起來也很輕松。
2.提高開發(fā)效率
憑借簡潔直觀的語法晶衷,Kotlin提高了團隊的效率蓝纲,編寫和部署程序需要更少的代碼行和時間。
3.強大的兼容
a.與 Java 可互操作房铭,可以在 Kotlin 中編寫新模塊驻龟,可以與現(xiàn)有 Java 代碼協(xié)同工作温眉;
b.最新版本的Kotlin與之前的所有版本都是反向兼容缸匪;Kotlin 兼容所有 Java 庫和框架以及JVM;
c.可以與 Gradle 或 Maven 構(gòu)建系統(tǒng)進行整合类溢;
d.idea和android studio都有對應(yīng)Java轉(zhuǎn)kotlin的插件凌蔬,原有的Java代碼可以直接轉(zhuǎn)換。
接下來的介紹就從和java的對比開始
類定義
java
public class Artist {
2 private long id;
3 private String name;
4 private String blog;
5
6 public long getId() {
7 return id;
8 }
9
10 public void setId(long id) {
11 this.id = id;
12 }
13
14 public String getName() {
15 return name;
16 }
17
18 public void setName(String name) {
19 this.name = name;
20 }
21
22 public String getBlog() {
23 return blog;
24 }
25
26 public void setBlog(String blog) {
27 this.blog = blog;
28 }
29
30 @Override public String toString() {
31 return "Artist{" +
32 "id=" + id +
33 ", name='" + name + '\'' +
34 ", blog='" + blog + '\'''}';
35 }
36 }
之前的話屬性+設(shè)置+獲取+toString()闯冷,一個數(shù)據(jù)類的基本功能就有了砂心,三個屬性的類寫了三十幾行代碼
kotlin
1 data class Artist(
2 var id: Long,
3 var name: String,
4 var blog: String
5 )
對象創(chuàng)建
Java:
1 Artist artist = new Artist(1, "Dylan", "http://www.cnblogs.com/tgyf/");
Kotlin:
1 var artist = Artist(1, "Dylan", "http://www.cnblogs.com/tgyf/")
Kotlin對象創(chuàng)建不用加new關(guān)鍵字,而且語句后面不用加分號";"(即使加上也會被忽略)蛇耀。
toString()
而類名前的data關(guān)鍵字辩诞,是顯示聲明該類是作為數(shù)據(jù)類使用,通過toString()打印的結(jié)果可以看出區(qū)別纺涤,打印語句:
1 println("artist.toString(): " + artist.toString())
不加data結(jié)果:
artist.toString(): Artist@61bbe9ba
加上data結(jié)果:
artist.toString(): Artist(id=1, name=Dylan, blog=http://www.cnblogs.com/tgyf/)
可以看到toString()是Kotlin自動生成的译暂,如果類聲明不加data抠忘,只會打印出一串?dāng)?shù)字(應(yīng)該是類的內(nèi)存地址),而不是當(dāng)前對象的屬性信息外永。
空安全
空指針這個錯誤不用多說崎脉,太常見了,Kotlin提供了一種安全機制伯顶,盡量減少變量在使用前是null的情況囚灼。
Java:
1 String str;
2 if (str != null) {
3 //do something
4 }
對于Java代碼,編譯器不會強制每次使用引用變量之前進行null判斷祭衩,即異常往往會在運行時報出灶体,但這正是危險所在。
Kotlin:
1 var str1: String = null //Null can not be a value of a non-null type String
2 var str2: String? = null //str2 can be null
3 var str3 = "testNull" //non-null--String type
4 var str4 = null //null
5 var str5: String //non-null--String type
6 str5 = "testNull" //assigned String value
7 var str6 //no type or initialization
8 var str7: String? = "testNull"
結(jié)合代碼中的注釋掐暮,我們來看這四行代碼想表達的意思赃春。
第1行,編譯錯誤劫乱,kotlin規(guī)定如果顯式指明了str1的類型织中,這里是String,聲明時必須同時指定是否允許為空值(null)衷戈,不加問號"?"表示不允許為null狭吼;
第2行,編譯通過殖妇,作第一行代碼的另一種情況刁笙,加了問號,并賦值為null谦趣;
第3行疲吸,編譯通過,隱式賦值為"testNull"前鹅,Kotlin會自動推斷出str3類型為String摘悴,之后便不可再更改了,即不可再賦值為1這種整形數(shù)據(jù)舰绘;
第4行蹂喻,編譯通過,隱式賦值為null捂寿,那么str4就一直為null了口四;
第5-6行,編譯通過秦陋,前者只是指定類型蔓彩,沒有賦值;后者賦予str5 String類型值"testNull"同樣不能賦值為其他類型值;
第7行赤嚼,編譯錯誤大磺,既沒有指定類型,也沒有隱式地進行初始化探膊,錯誤的原因應(yīng)該是編譯器不知道str6類型是什么杠愧,不能對其分配空間;
第8行逞壁,不需多解釋流济,str7可為null,同時賦值為"testNull"腌闯;
注意:此文為了格式統(tǒng)一绳瘟,沒有將編譯或運行出錯的代碼注釋,分享的項目代碼中是可以正常編譯并運行的姿骏。
解釋完變量定義時關(guān)于空的概念糖声,接下來就該看看這種保護機制能否真的讓我們省心。就拿獲取字串的長度為例分瘦,Kotlin中String類有個length屬性蘸泻,即調(diào)用方式為strObject.length。
有兩種形式定義的變量不用擔(dān)心(1 類型為String且不允許為null嘲玫;2 類中不包含length屬性)悦施, 理由很簡單,前者不會出現(xiàn)null異常去团,后者獲取length屬性在編譯階段就會出錯抡诞,或者說在敲完代碼時編譯器就會標(biāo)紅提示了。所以土陪,String類型但允許為null的才需要我們關(guān)注昼汗,因為這時候有可能出現(xiàn)運行時異常。
對于聲明為String?的變量鬼雀,訪問屬性時會涉及到問號和雙感嘆號兩個操作符("?"和"!!")顷窒,前者表示執(zhí)行后面代碼前先檢查變量賦值情況,后者表示不檢查而直接訪問屬性(危險)取刃。
要理解清楚蹋肮,最好的方法就是讓代碼說話。
1 var str2: String? = null
2 println("str2.length: " + str2.length) //compile error
3 println("str2?.length: " + str2?.length) //print null
4 println("str2!!.length: " + str2!!.length) //run exception
5 if (str2 != null) {
6 println("str2!!.length: " + str2!!.length) //don't run
7 }
8 str2 = "testNull" //assign
9 println("str2.length: " + str2.length) //print 8
10 println("str2?.length: " + str2?.length) //print 8
11 println("str2!!.length: " + str2!!.length) //print 8
12 if (str2 != null) {
13 println("str2!!.length: " + str2.length) //print 8
14 }
第2行璧疗,編譯錯誤,因為之前只是將str2聲明為可以是null同時賦值為null馁龟,所以緊接著訪問其length屬性是不允許的崩侠;
第3行,輸出"null"坷檩,加了問號就會先檢查str2的賦值情況却音,如果是null改抡,就不繼續(xù)執(zhí)行后半部分(.length),直接返回null系瓢;
第4行阿纤,運行異常,不檢查的后果就是通過null引用去訪問length屬性夷陋;
第5-7行欠拾,不會執(zhí)行到if代碼塊中,這里用了類似Java中的做法骗绕;
第9行藐窄,輸出"8",到這里酬土,相比能體會到Kotlin的智能之處了荆忍,在第八行對str2賦值之后,就不會再像第二行那樣報編譯錯誤了撤缴;
第10-14行刹枉,不需多解釋,不為null的str2屈呕,通過三種方式均可訪問length屬性嘶卧;
那么這里有一個疑問,用"!!"來訪問屬性是不明智的選擇凉袱,好像"?"更穩(wěn)妥一些芥吟?畢竟后者在變量是否null的情況下都能做出相應(yīng)的處理。我所能想到的需要用"!!"的場景之一是:當(dāng)一個變量在聲明時不能馬上初始化专甩,而在真正用到時又必須是非null的钟鸵。這種情況應(yīng)該并不少見吧,那次此時"!!"就派上用場了涤躲。
先舉一個簡單粗暴的列子:
1 var str: String? = null
2 //do something to assign str
3 val str2: String = str!!
當(dāng)聲明str的時候還需后面的處理結(jié)果給它賦值棺耍,而聲明str2為非null,就必須以str!!的形式才能通過編譯种樱。
下面再給出Android中Application類單例化代碼蒙袍,就不做解釋了。
1 class App : Application() {
2 companion object {
3 private var instance: Application? = null
4 fun instance() = instance!!
5 }
6 override fun onCreate() {
7 super.onCreate()
8 instance = this
9 }
10 }
類方法擴展
這個特性支持在現(xiàn)有類的基礎(chǔ)上擴展方法嫩挤,特別是系統(tǒng)庫中的類害幅,因為如果是我們自定義的類,那么擴展和添加方法沒有什么差別岂昭。
方法定義
1 fun getArtict(): Artist? {
2 return null
3 }
Kotlin中是以fun關(guān)鍵字聲明方法以现,沒有返回值時不需要在方法名后面寫任何類型,默認(rèn)是Unit類型(可寫可不寫,但其和null不是一回事邑遏,所以不寫返回值類型或者寫了Unit后不能夠返回null)佣赖。
擴展
1 fun String.printStr() {
2 println("printStr: " + this)
3 }
4
5 var str = "testExtend"
6 str.printStr()
上面代碼為類String擴展了一個printStr(),這在Java中是不可能的记盒。因為Java中如果既不能改變原有類憎蛤,又想在其基礎(chǔ)上添加方法,就得通過新建類來繼承的方式纪吮。而現(xiàn)實是Java中只能是單繼承俩檬,這個機會太珍貴了,更殘酷的是有些類還是不能繼承的彬碱。
代碼第5-6行執(zhí)行結(jié)果為:
printStr: testExtend
可見豆胸,通過this關(guān)鍵字即可獲取到對象(調(diào)用者)的值。
lambda表達式
這部分測試代碼沒有在分享的項目中巷疼,因為涉及到Android開發(fā)晚胡,需要在Android項目中才能編譯或運行,可以參考這篇嚼沿。
下面以綁定控件估盘,設(shè)置按鈕點擊事件監(jiān)聽,點擊后改變文本顯示為例骡尽。
Java:
1 Button button = (Button) findViewById(R.id.button);
2 TextView text = (TextView) findViewById(R.id.text);
3 button.setOnClickListener(new View.OnClickListener() {
4
5 @Override
6 public void onClick(View v) {
7 text.setText("Set text after click button");
8 }
9 });
做過Android開發(fā)的對這段代碼太熟悉了遣妥,盡管目前已經(jīng)出了很多開源庫,比如ButterKnife等可以不必使用findViewById()而實現(xiàn)快速綁定攀细,但畢竟還是需要手動綁定這一步箫踩。
Kotlin:
1 button.setOnClickListener {
2 text.setText("Set text after click button")
3 text.text = "Set text after click button"
4 }
其中,buttonR.id.button谭贪,第一個textR.id.text境钟,第二個text~TextView顯示文本。第2-3行是設(shè)置文本的兩種方式俭识,Kotlin建議用更簡潔的第二種.text慨削,這也是文章開頭定義數(shù)據(jù)類時屬性采用默認(rèn)訪問修飾的原因,因為private屬性就不能直接通過"."直接獲取了套媚。
如果遇到多個Button需要共享一個onClick()怎么辦呢缚态?Java代碼就不給出了,來看Kotlin代碼:
1 button1.setOnClickListener(this)
2 button2.setOnClickListener(this)
3 button3.setOnClickListener(this)
4
5 override fun onClick(view: View) {
6 val id = view.id
7 when (id) {
8 R.id.button1 -> selectImageBtn()
9 R.id.button2 -> clearImageBtn()
10 R.id.button3 -> sendBulletinBtn()
11 else -> { }
12 }
13 }
第1-3行堤瘤,除了不需要調(diào)用findViewById()來獲取控件玫芦,設(shè)置事件監(jiān)聽和Java是類似的;
第5-13行宙橱,重寫關(guān)鍵字override姨俩,前面不必寫"@"符號蘸拔,用when师郑、->及else組合來替代原先的switch环葵、case及default,再也不用為每種case的結(jié)尾寫上break宝冕。
總結(jié)
這篇文章先寫這么多吧张遭,提到的知識僅是Kotlin的九牛一毛,我覺得作為開發(fā)者,學(xué)習(xí)是很有必要的地梨,在熟練掌握了某種語言的基礎(chǔ)上菊卷,學(xué)習(xí)新的語言,有助于提升個人競爭力宝剖。