編程語(yǔ)言之多,讓人眼花繚亂。你可能不知道,世界上一共誕生過(guò)600多門(mén)有記錄的編程語(yǔ)言, 沒(méi)有記錄的那就更多了。這些編程語(yǔ)言基本上共有的特性就是變量和函數(shù)。可以說(shuō),變量和函 數(shù)就是編程語(yǔ)言之本。那么本節(jié)我們就來(lái)學(xué)習(xí)一下Kotlin中變量和函數(shù)的用法。
1.變量
先來(lái)學(xué)習(xí)變量。在Kotlin中定義變量的方式和Java區(qū)別很大,在Java中如果想要定義一個(gè)變 量,需要在變量前面聲明這個(gè)變量的類(lèi)型,比如說(shuō)int a表示a是一個(gè)整型變量,String b表 示b是一個(gè)字符串變量。而Kotlin中定義一個(gè)變量,只允許在變量前聲明兩種關(guān)鍵字:val和 var。
val(value的簡(jiǎn)寫(xiě))用來(lái)聲明一個(gè)不可變的變量,這種變量在初始賦值之后就再也不能重新賦 值,對(duì)應(yīng)Java中的final變量。
var(variable的簡(jiǎn)寫(xiě))用來(lái)聲明一個(gè)可變的變量,這種變量在初始賦值之后仍然可以再被重新 賦值,對(duì)應(yīng)Java中的非final變量。
如果你有Java編程經(jīng)驗(yàn)的話,可能會(huì)在這里產(chǎn)生疑惑,僅僅使用val或者var來(lái)聲明一個(gè)變量, 那么編譯器怎么能知道這個(gè)變量是什么類(lèi)型呢?這也是Kotlin比較有特色的一點(diǎn),它擁有出色的 類(lèi)型推導(dǎo)機(jī)制。
舉個(gè)例子,我們?cè)谏弦徽碌膭?chuàng)建的kt文件中,編寫(xiě)如下代碼:
fun main() {
val a = 10
println("a = " + a)
}
注意,Kotlin每一行代碼的結(jié)尾是不用加分號(hào)的,如果你寫(xiě)慣了Java的話,在這里得先熟悉一 下。
在上述代碼中,我們使用val關(guān)鍵字定義了一個(gè)變量a,并將它賦值為10,這里a就會(huì)被自動(dòng)推 導(dǎo)成整型變量。因?yàn)榧热荒阋岩粋€(gè)整數(shù)賦值給a,那么a就只能是整型變量,而如果你要把一 個(gè)字符串賦值給a的話,那么a就會(huì)被自動(dòng)推導(dǎo)成字符串變量,這就是Kotlin的類(lèi)型推導(dǎo)機(jī)制。
現(xiàn)在我們運(yùn)行一下main()函數(shù),執(zhí)行結(jié)果如下圖1所示,正是我們所預(yù)期的。
但是Kotlin的類(lèi)型推導(dǎo)機(jī)制并不總是可以正常工作的,比如說(shuō)如果我們對(duì)一個(gè)變量延遲賦值【1】的 話,Kotlin就無(wú)法自動(dòng)推導(dǎo)它的類(lèi)型了。這時(shí)候就需要顯式地聲明變量類(lèi)型才行,Kotlin提供了 對(duì)這一功能的支持,語(yǔ)法如下所示:
【1】變量延遲賦值:立即賦值是將等號(hào)右邊的表達(dá)式計(jì)算過(guò)后再賦給式子的左邊,延遲賦值則是不計(jì)算右邊的式子,在后續(xù)代碼中要調(diào)用左側(cè)的變量時(shí)才計(jì)算。
val a: Int = 10
可以看到,我們顯式地聲明了變量a為Int類(lèi)型,此時(shí)Kotlin就不會(huì)再?lài)L試進(jìn)行類(lèi)型推導(dǎo)了。如 果現(xiàn)在你嘗試將一個(gè)字符串賦值給a,那么編譯器就會(huì)拋出類(lèi)型不匹配的異常。
如果你學(xué)過(guò)Java并且足夠細(xì)心的話,你可能發(fā)現(xiàn)了Kotlin中Int的首字母是大寫(xiě)的,而Java中 int的首字母是小寫(xiě)的。不要小看這一個(gè)字母大小寫(xiě)的差距,這表示Kotlin完全拋棄了Java中的 基本數(shù)據(jù)類(lèi)型,全部使用了對(duì)象數(shù)據(jù)類(lèi)型。在Java中int是關(guān)鍵字,而在Kotlin中Int變成了一 個(gè)類(lèi),它擁有自己的方法和繼承結(jié)構(gòu)。下表中列出了Java中的每一個(gè)基本數(shù)據(jù)類(lèi)型在Kotlin中 對(duì)應(yīng)的對(duì)象數(shù)據(jù)類(lèi)型。
接下來(lái)我們嘗試對(duì)變量a進(jìn)行一些數(shù)學(xué)運(yùn)算,比如說(shuō)讓a變大10倍,可能你會(huì)很自然地寫(xiě)出如下 代碼:
fun main() {
val a: Int = 10
a = a * 10
println("a = " + a)
}
很遺憾,如果你這樣寫(xiě)的話,編譯器一定會(huì)提示一個(gè)錯(cuò)誤:Val cannot be reassigned。這是 在告訴我們,使用val關(guān)鍵字聲明的變量無(wú)法被重新賦值。出現(xiàn)這個(gè)問(wèn)題的原因是我們?cè)谝婚_(kāi)始 定義a的時(shí)候?qū)⑺x值成了10,然后又在下一行讓它變大10倍,這個(gè)時(shí)候就是對(duì)a進(jìn)行重新賦值 了,因而編譯器也就報(bào)錯(cuò)了。
解決這個(gè)問(wèn)題的辦法也很簡(jiǎn)單,前面已經(jīng)提到了,val關(guān)鍵字用來(lái)聲明一個(gè)不可變的變量,而 var關(guān)鍵字用來(lái)聲明一個(gè)可變的變量,所以這里只需要把val改成var即可,如下所示:
fun main() {
var a: Int = 10
a = a * 10
println("a = " + a)
}
現(xiàn)在編譯器就不會(huì)再報(bào)錯(cuò)了,重新運(yùn)行一下代碼,結(jié)果下圖所示。
可以看到a的值變成了100,這說(shuō)明我們的數(shù)學(xué)運(yùn)算操作成功了。
這里你可能會(huì)產(chǎn)生疑惑:既然val關(guān)鍵字有這么多的束縛,為什么還要用這個(gè)關(guān)鍵字呢?干脆全 部用var關(guān)鍵字不就好了。其實(shí)Kotlin之所以這樣設(shè)計(jì),是為了解決Java中final關(guān)鍵字沒(méi)有被 合理使用的問(wèn)題。
在Java中,除非你主動(dòng)在變量前聲明了final關(guān)鍵字,否則這個(gè)變量就是可變的。然而這并不 是一件好事,當(dāng)項(xiàng)目變得越來(lái)越復(fù)雜,參與開(kāi)發(fā)的人越來(lái)越多時(shí),你永遠(yuǎn)不知道一個(gè)可變的變 量會(huì)在什么時(shí)候被誰(shuí)給修改了,即使它原本不應(yīng)該被修改,這就經(jīng)常會(huì)導(dǎo)致出現(xiàn)一些很難排查 的問(wèn)題。因此,一個(gè)好的編程習(xí)慣是,除非一個(gè)變量明確允許被修改,否則都應(yīng)該給它加上 final關(guān)鍵字。
但是,不是每個(gè)人都能養(yǎng)成這種良好的編程習(xí)慣。我相信至少有90%的Java程序員沒(méi)有主動(dòng)在 變量前加上final關(guān)鍵字的意識(shí),僅僅因?yàn)镴ava對(duì)此是不強(qiáng)制的。因此,Kotlin在設(shè)計(jì)的時(shí)候就 采用了和Java完全不同的方式,提供了val和var這兩個(gè)關(guān)鍵字,必須由開(kāi)發(fā)者主動(dòng)聲明該變量 是可變的還是不可變的。
那么我們應(yīng)該什么時(shí)候使用val,什么時(shí)候使用var呢?這里我告訴你一個(gè)小訣竅,就是永遠(yuǎn)優(yōu) 先使用val來(lái)聲明一個(gè)變量,而當(dāng)val沒(méi)有辦法滿足你的需求時(shí)再使用var。這樣設(shè)計(jì)出來(lái)的程 序會(huì)更加健壯,也更加符合高質(zhì)量的編碼規(guī)范。 不少剛接觸編程的人對(duì)于函數(shù)和方法這兩個(gè)概念有些混淆,不明白它們有什么區(qū)別。其實(shí),函 數(shù)和方法就是同一個(gè)概念,這兩種叫法都是從英文翻譯過(guò)來(lái)的,函數(shù)翻譯自function,方法翻 譯自method,它們并沒(méi)有什么區(qū)別,只是不同語(yǔ)言的叫法習(xí)慣不一樣而已。而因?yàn)镴ava中方 法的叫法更普遍一些,Kotlin中函數(shù)的叫法更普遍一些,因此本書(shū)里可能會(huì)交叉使用兩種叫法, 你只要知道它們是同一種東西就可以了,不用在這個(gè)地方產(chǎn)生疑惑。 函數(shù)是用來(lái)運(yùn)行代碼的載體,你可以在一個(gè)函數(shù)里編寫(xiě)很多行代碼,當(dāng)運(yùn)行這個(gè)函數(shù)時(shí),函數(shù) 中的所有代碼會(huì)全部運(yùn)行。像我們前面使用過(guò)的main()函數(shù)就是一個(gè)函數(shù),只不過(guò)它比較特 殊,是程序的入口函數(shù),即程序一旦運(yùn)行,就是從main()函數(shù)開(kāi)始執(zhí)行的。 但是只有一個(gè)main()函數(shù)的程序顯然是很初級(jí)的,和其他編程語(yǔ)言一樣,Kotlin也允許我們自 由地定義函數(shù),語(yǔ)法規(guī)則如下:
2.函數(shù)
不少剛接觸編程的人對(duì)于函數(shù)和方法這兩個(gè)概念有些混淆,不明白它們有什么區(qū)別。其實(shí),函 數(shù)和方法就是同一個(gè)概念,這兩種叫法都是從英文翻譯過(guò)來(lái)的,函數(shù)翻譯自function,方法翻 譯自method,它們并沒(méi)有什么區(qū)別,只是不同語(yǔ)言的叫法習(xí)慣不一樣而已。而因?yàn)镴ava中方 法的叫法更普遍一些,Kotlin中函數(shù)的叫法更普遍一些,因此本書(shū)里可能會(huì)交叉使用兩種叫法, 你只要知道它們是同一種東西就可以了,不用在這個(gè)地方產(chǎn)生疑惑。
函數(shù)是用來(lái)運(yùn)行代碼的載體,你可以在一個(gè)函數(shù)里編寫(xiě)很多行代碼,當(dāng)運(yùn)行這個(gè)函數(shù)時(shí),函數(shù) 中的所有代碼會(huì)全部運(yùn)行。像我們前面使用過(guò)的main()函數(shù)就是一個(gè)函數(shù),只不過(guò)它比較特 殊,是程序的入口函數(shù),即程序一旦運(yùn)行,就是從main()函數(shù)開(kāi)始執(zhí)行的。
但是只有一個(gè)main()函數(shù)的程序顯然是很初級(jí)的,和其他編程語(yǔ)言一樣,Kotlin也允許我們自 由地定義函數(shù),語(yǔ)法規(guī)則如下:
fun methodName(param1: Int, param2: Int): Int {
return 0
}
下面我來(lái)解釋一下上述的語(yǔ)法規(guī)則,首先f(wàn)un(function的簡(jiǎn)寫(xiě))是定義函數(shù)的關(guān)鍵字,無(wú)論你 定義什么函數(shù),都一定要使用fun來(lái)聲明。
緊跟在fun后面的是函數(shù)名,這個(gè)就沒(méi)有什么要求了,你可以根據(jù)自己的喜好起任何名字,但是 良好的編程習(xí)慣是函數(shù)名最好要有一定的意義,能表達(dá)這個(gè)函數(shù)的作用是什么。
函數(shù)名后面緊跟著一對(duì)括號(hào),里面可以聲明該函數(shù)接收什么參數(shù),參數(shù)的數(shù)量可以是任意多個(gè),例如上述示例就表示該函數(shù)接收兩個(gè)Int類(lèi)型的參數(shù)。參數(shù)的聲明格式是“參數(shù)名: 參數(shù)類(lèi)型”,其中參數(shù)名也是可以隨便定義的,這一點(diǎn)和函數(shù)名類(lèi)似。如果不想接收任何參數(shù),那么寫(xiě)一對(duì)空括號(hào)就可以了。
參數(shù)括號(hào)后面的那部分是可選的,用于聲明該函數(shù)會(huì)返回什么類(lèi)型的數(shù)據(jù),上述示例就表示該函數(shù)會(huì)返回一個(gè)Int類(lèi)型的數(shù)據(jù)。如果你的函數(shù)不需要返回任何數(shù)據(jù),這部分可以直接不寫(xiě)。最后兩個(gè)大括號(hào)之間的內(nèi)容就是函數(shù)體了,我們可以在這里編寫(xiě)一個(gè)函數(shù)的具體邏輯。由于上述示例中聲明了該函數(shù)會(huì)返回一個(gè)Int類(lèi)型的數(shù)據(jù),因此在函數(shù)體中我們簡(jiǎn)單地返回了一個(gè)0。
這就是定義一個(gè)函數(shù)最標(biāo)準(zhǔn)的方式了,雖然Kotlin中還有許多其他修飾函數(shù)的關(guān)鍵字,但是只要掌握了上述函數(shù)定義規(guī)則,你就已經(jīng)能應(yīng)對(duì)80%以上的編程場(chǎng)景了,至于其他的關(guān)鍵字,我們會(huì)在后面慢慢學(xué)習(xí)。
接下來(lái)我們嘗試按照上述定義函數(shù)的語(yǔ)法規(guī)則來(lái)定義一個(gè)有意義的函數(shù),如下所示:
fun largerNumber(num1: Int, num2: Int): Int {
return max(num1, num2)
}
這里定義了一個(gè)名叫test()的函數(shù),該函數(shù)的作用很簡(jiǎn)單,接收兩個(gè)整型參數(shù),然 后總是返回兩個(gè)參數(shù)中更大的那個(gè)數(shù)。
注意,上述代碼中使用了一個(gè)max()函數(shù),這是Kotlin提供的一個(gè)內(nèi)置函數(shù),它的作用就是返回 兩個(gè)參數(shù)中更大的那個(gè)數(shù),因此我們的test()函數(shù)其實(shí)就是對(duì)max()函數(shù)做了一層 封裝而已。
現(xiàn)在你可以開(kāi)始在LearnKotlin文件中實(shí)現(xiàn)test()這個(gè)函數(shù)了,當(dāng)你輸入“max”這 個(gè)單詞時(shí),Android Studio會(huì)自動(dòng)彈出如下圖所示的代碼提示。
Android Studio擁有非常智能的代碼提示和補(bǔ)全功能,通常你只需要鍵入部分代碼,它就能自動(dòng)預(yù)測(cè)你想要編寫(xiě)的內(nèi)容,并給出相應(yīng)的提示列表。我們可以通過(guò)上下鍵在提示列表中移動(dòng),然后按下“Enter”鍵,Android Studio就會(huì)自動(dòng)幫我們進(jìn)行代碼補(bǔ)全了。
這里我非常建議你經(jīng)常使用Android Studio的代碼補(bǔ)全功能,可能有些人覺(jué)得全部手敲更有成就感,但是我要提醒一句,使用代碼補(bǔ)全功能后,Android Studio不僅會(huì)幫我們補(bǔ)全代碼,還會(huì)幫我們自動(dòng)導(dǎo)包,這一點(diǎn)是很重要的。比如說(shuō)上述的max()函數(shù),如果你全部手敲出來(lái),那么這個(gè)函數(shù)一定會(huì)提示一個(gè)紅色的錯(cuò)誤,如下圖所示。
出現(xiàn)這個(gè)錯(cuò)誤的原因是你沒(méi)有導(dǎo)入max()函數(shù)的包。當(dāng)然,導(dǎo)包的方法也有很多種,你將光標(biāo) 移動(dòng)到這個(gè)紅色的錯(cuò)誤上面就能看到導(dǎo)包的快捷鍵提示,但是最好的做法就是使用Android Studio的代碼補(bǔ)全功能,這樣導(dǎo)包工作就自動(dòng)完成了。
現(xiàn)在我們使用代碼補(bǔ)全功能(alt+enter)再來(lái)編寫(xiě)一次max()函數(shù),你會(huì)發(fā)現(xiàn)LearnKotlin文件的頭部自動(dòng)導(dǎo)入了一個(gè)max()函數(shù)的包,并且不會(huì)再有錯(cuò)誤提示了,如下圖所示。
現(xiàn)在test()函數(shù)已經(jīng)編寫(xiě)好了,接下來(lái)我們可以嘗試在main()函數(shù)中調(diào)用這個(gè)函數(shù),并且實(shí)現(xiàn)在兩個(gè)數(shù)中找到較大的那個(gè)數(shù)這樣一個(gè)簡(jiǎn)單的功能,代碼如下所示:
fun main() {
val a = 37
val b = 43
var value :Int
value = test(a, b)
println("larger number is " + value)
}
fun test(int1: Int,int2: Int): Int {
return max(int1,int2)
}
這段代碼很簡(jiǎn)單,我們定義了a、b兩個(gè)變量,a的值是37,b的值是40,然后調(diào)用test()函數(shù),并將a、b作為參數(shù)傳入。test()函數(shù)會(huì)返回這兩個(gè)變量中較大的那個(gè)數(shù),最后將返回值打印出來(lái)。現(xiàn)在運(yùn)行一下代碼,結(jié)果下圖所示。程序正如我們預(yù)期的那樣運(yùn)行了。
這就是Kotlin中最基本也是最常用的函數(shù)用法,雖然這里我們實(shí)現(xiàn)的largerNumber()函數(shù)很 簡(jiǎn)單,但是掌握了函數(shù)的定義規(guī)則之后,你想實(shí)現(xiàn)多么復(fù)雜的函數(shù)都是可以的。
3.拓展內(nèi)容
我們?cè)賮?lái)學(xué)習(xí)一個(gè)Kotlin函數(shù)的語(yǔ)法糖,這個(gè)語(yǔ)法糖在以后的開(kāi)發(fā)中會(huì)起到相當(dāng)重要的作用。
當(dāng)一個(gè)函數(shù)中只有一行代碼時(shí),Kotlin允許我們不必編寫(xiě)函數(shù)體,可以直接將唯一的一行代碼寫(xiě)在函數(shù)定義的尾部,中間用等號(hào)連接即可。比如我們剛才編寫(xiě)的test()函數(shù)就只有一行代碼,于是可以將代碼簡(jiǎn)化成如下形式:
fun main() {
val a = 37
val b = 43
var value = test(a, b)
println("larger number is " + value)
}
//注意看這里,原定義的函數(shù)變成只有1行了
fun test(int1: Int,int2: Int): Int = max(int1,int2)
使用這種語(yǔ)法,return關(guān)鍵字也可以省略了,等號(hào)足以表達(dá)返回值的意思。另外,還記得Kotlin出色的類(lèi)型推導(dǎo)機(jī)制嗎?在這里它也可以發(fā)揮重要的作用。由于max()函數(shù)返回的是一個(gè)Int值,而我們?cè)趖est()函數(shù)的尾部又使用等號(hào)連接了max()函數(shù),因此Kotlin可以推導(dǎo)出test()函數(shù)返回的必然也是一個(gè)Int值,這樣就不用再顯式地聲明返回值類(lèi)型了,代碼可以進(jìn)一步簡(jiǎn)化成如下形式:
fun test(int1: Int,int2: Int) = max(int1,int2)
可能你會(huì)覺(jué)得,函數(shù)只有一行代碼的情況并不多嘛,這個(gè)語(yǔ)法糖也不會(huì)很常用吧?其實(shí)并不是這樣的,因?yàn)樗€可以結(jié)合Kotlin的其他語(yǔ)言特性一起使用,對(duì)簡(jiǎn)化代碼方面的幫助很大,后面我們會(huì)慢慢學(xué)習(xí)它更多的使用場(chǎng)景。
《高級(jí)Kotlin強(qiáng)化實(shí)戰(zhàn)》
第一章 Kotlin入門(mén)教程
Kotlin 概述
Kotlin 與 Java 比較
巧用 Android Studio
認(rèn)識(shí) Kotlin 基本類(lèi)型
走進(jìn) Kotlin 的數(shù)組
走進(jìn) Kotlin 的集合
集合問(wèn)題
完整代碼
基礎(chǔ)語(yǔ)法
第二章 Kotlin 實(shí)戰(zhàn)避坑指南
方法入?yún)⑹浅A浚豢尚薷?/p>
不要 Companion 、INSTANCE ?
Java 重載,在 Kotlin 中怎么巧妙過(guò)渡一下?
Kotlin 中的判空姿勢(shì)
Kotlin 復(fù)寫(xiě) Java 父類(lèi)中的方法
Kotlin “狠”起來(lái),連TODO 都不放過(guò)!
is、as` 中的坑
Kotlin 中的 Property 的理解
also 關(guān)鍵字
takeIf 關(guān)鍵字
takeIf 關(guān)鍵字
單例模式的寫(xiě)法
第三章 項(xiàng)目實(shí)戰(zhàn)《Kotlin Jetpack 實(shí)戰(zhàn)》
從一個(gè)膜拜大神的 Demo 開(kāi)始
Kotlin 寫(xiě) Gradle 腳本是一種什么體驗(yàn)?
Kotlin 編程的三重境界
Kotlin 高階函數(shù)
Kotlin 泛型
Kotlin 擴(kuò)展
Kotlin 委托
協(xié)程“不為人知”的調(diào)試技巧
圖解協(xié)程:suspend
s信獲取zi料~