Java轉(zhuǎn)Kotlin學(xué)習(xí)(一)

至今都不敢相信,我有一天會寫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提高了團(tuán)隊的效率,編寫和部署程序需要更少的代碼行和時間。
3.強(qiáng)大的兼容
a.與 Java 可互操作,可以在 Kotlin 中編寫新模塊,可以與現(xiàn)有 Java 代碼協(xié)同工作;
b.最新版本的Kotlin與之前的所有版本都是反向兼容;Kotlin 兼容所有 Java 庫和框架以及JVM;
c.可以與 Gradle 或 Maven 構(gòu)建系統(tǒng)進(jìn)行整合;
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提供了一種安全機(jī)制,盡量減少變量在使用前是null的情況。

Java:

1 String str;
2 if (str != null) {
3     //do something
4 }

對于Java代碼,編譯器不會強(qiáng)制每次使用引用變量之前進(jìn)行null判斷,即異常往往會在運(yùn)行時報出,但這正是危險所在。

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é)合代碼中的注釋,我們來看這四行代碼想表達(dá)的意思。

第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行,編譯錯誤,既沒有指定類型,也沒有隱式地進(jìn)行初始化,錯誤的原因應(yīng)該是編譯器不知道str6類型是什么,不能對其分配空間;

第8行,不需多解釋,str7可為null,同時賦值為"testNull";

注意:此文為了格式統(tǒng)一,沒有將編譯或運(yùn)行出錯的代碼注釋,分享的項目代碼中是可以正常編譯并運(yùn)行的。

解釋完變量定義時關(guān)于空的概念,接下來就該看看這種保護(hù)機(jī)制能否真的讓我們省心。就拿獲取字串的長度為例,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)運(yù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行,運(yùn)行異常,不檢查的后果就是通過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 }

類方法擴(kuò)展

這個特性支持在現(xiàn)有類的基礎(chǔ)上擴(kuò)展方法,特別是系統(tǒng)庫中的類,因為如果是我們自定義的類,那么擴(kuò)展和添加方法沒有什么差別。

方法定義

1 fun getArtict(): Artist? {
2     return null
3 }

Kotlin中是以fun關(guān)鍵字聲明方法,沒有返回值時不需要在方法名后面寫任何類型,默認(rèn)是Unit類型(可寫可不寫,但其和null不是一回事,所以不寫返回值類型或者寫了Unit后不能夠返回null)。

擴(kuò)展

1 fun String.printStr() {
2     println("printStr: " + this)
3 }
4 
5 var str = "testExtend"
6 str.printStr()

上面代碼為類String擴(kuò)展了一個printStr(),這在Java中是不可能的。因為Java中如果既不能改變原有類,又想在其基礎(chǔ)上添加方法,就得通過新建類來繼承的方式。而現(xiàn)實是Java中只能是單繼承,這個機(jī)會太珍貴了,更殘酷的是有些類還是不能繼承的。

代碼第5-6行執(zhí)行結(jié)果為:

printStr: testExtend

可見,通過this關(guān)鍵字即可獲取到對象(調(diào)用者)的值。

lambda表達(dá)式

這部分測試代碼沒有在分享的項目中,因為涉及到Android開發(fā),需要在Android項目中才能編譯或運(yùn)行,可以參考這篇。

下面以綁定控件,設(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í)新的語言,有助于提升個人競爭力。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容