在Kotlin中定義變量的方式和Java 區別很大,在Java 中如果想要定義一個變
量,需要在變量前面聲明這個變量的類型,比如說int a表示a是一個整型變量,String b表
示b是一個字符串變量。而Kotlin中定義一個變量,只允許在變量前聲明兩種關鍵字:val和
var。
val(value 的簡寫)用來聲明一個不可變的變量,這種變量在初始賦值之后就再也不能重新賦
值,對應Java 中的final變量。
var(variable 的簡寫)用來聲明一個可變的變量,這種變量在初始賦值之后仍然可以再被重新
賦值,對應Java 中的非final變量。
如果你有Java 編程經驗的話,可能會在這里產生疑惑,僅僅使用val或者var來聲明一個變量,
那么編譯器怎么能知道這個變量是什么類型呢?這也是Kotlin比較有特色的一點,它擁有出色的
類型推導機制。
舉個例子,我們打開上一節創建的LearnKotlin 文件,在main()函數中編寫如下代碼:
fun main() { val a = 10 println("a = " + a)
}
注意,Kotlin每一行代碼的結尾是不用加分號的,如果你寫慣了Java 的話,在這里得先熟悉一
下。
在上述代碼中,我們使用val關鍵字定義了一個變量a,并將它賦值為10 ,這里a就會被自動推
導成整型變量。因為既然你要把一個整數賦值給a,那么a就只能是整型變量,而如果你要把一
個字符串賦值給a的話,那么a就會被自動推導成字符串變量,這就是Kotlin的類型推導機制。
現在我們運行一下main()函數,執行結果如圖所示,正是我們所預期的。
但是Kotlin的類型推導機制并不總是可以正常工作的,比如說如果我們對一個變量延遲賦值的
話,Kotlin就無法自動推導它的類型了。這時候就需要顯式地聲明變量類型才行,Kotlin提供了
對這一功能的支持,語法如下所示:
val a: Int = 10
可以看到,我們顯式地聲明了變量a為Int類型,此時Kotlin就不會再嘗試進行類型推導了。如
果現在你嘗試將一個字符串賦值給a,那么編譯器就會拋出類型不匹配的異常。
如果你學過Java 并且足夠細心的話,你可能發現了Kotlin中Int的首字母是大寫的,而Java 中
int的首字母是小寫的。不要小看這一個字母大小寫的差距,這表示Kotlin完全拋棄了Java 中的
基本數據類型,全部使用了對象數據類型。在Java 中int是關鍵字,而在Kotlin中Int變成了一
個類,它擁有自己的方法和繼承結構。
表中列出了Java 中的每一個基本數據類型在Kotlin中對應的對象數據類型。
接下來我們嘗試對變量a進行一些數學運算,比如說讓a變大10 倍,可能你會很自然地寫出如下
代碼:
fun main() { val a: Int = 10 a = a * 10 println("a = " + a)
}
很遺憾,如果你這樣寫的話,編譯器一定會提示一個錯誤:Val cannot be reassigned 。這是
在告訴我們,使用val關鍵字聲明的變量無法被重新賦值。出現這個問題的原因是我們在一開始
定義a的時候將它賦值成了10 ,然后又在下一行讓它變大10 倍,這個時候就是對a進行重新賦值
了,因而編譯器也就報錯了。
解決這個問題的辦法也很簡單,前面已經提到了,val關鍵字用來聲明一個不可變的變量,而
var關鍵字用來聲明一個可變的變量,所以這里只需要把val改成var即可,如下所示:
fun main() { var a: Int = 10 a = a * 10 println("a = " + a)
}
現在編譯器就不會再報錯了,重新運行一下代碼
可以看到,a的值變成了100 ,這說明我們的數學運算操作成功了。
這里你可能會產生疑惑:既然val關鍵字有這么多的束縛,為什么還要用這個關鍵字呢?干脆全
部用var關鍵字不就好了。其實Kotlin之所以這樣設計,是為了解決Java 中final關鍵字沒有被
合理使用的問題。
在Java 中,除非你主動在變量前聲明了final關鍵字,否則這個變量就是可變的。然而這并不
是一件好事,當項目變得越來越復雜,參與開發的人越來越多時,你永遠不知道一個可變的變
量會在什么時候被誰給修改了,即使它原本不應該被修改,這就經常會導致出現一些很難排查
的問題。因此,一個好的編程習慣是,除非一個變量明確允許被修改,否則都應該給它加上
final關鍵字。
但是,不是每個人都能養成這種良好的編程習慣。我相信至少有90% 的Java 程序員沒有主動在
變量前加上final關鍵字的意識,僅僅因為Java 對此是不強制的。因此,Kotlin在設計的時候就
采用了和Java 完全不同的方式,提供了val和var這兩個關鍵字,必須由開發者主動聲明該變量
是可變的還是不可變的。
那么我們應該什么時候使用val,什么時候使用var呢?這里我告訴你一個小訣竅,就是永遠優
先使用val來聲明一個變量,而當val沒有辦法滿足你的需求時再使用var。這樣設計出來的程
序會更加健壯,也更加符合高質量的編碼規范。
函數
對于函數和方法這兩個概念有些混淆,不明白它們有什么區別。其實,函
數和方法就是同一個概念,這兩種叫法都是從英文翻譯過來的,函數翻譯自function ,方法翻
譯自method ,它們并沒有什么區別,只是不同語言的叫法習慣不一樣而已。而因為Java 中方
法的叫法更普遍一些,Kotlin中函數的叫法更普遍一些,因此本書里可能會交叉使用兩種叫法,
你只要知道它們是同一種東西就可以了,不用在這個地方產生疑惑。
函數是用來運行代碼的載體,你可以在一個函數里編寫很多行代碼,當運行這個函數時,函數
中的所有代碼會全部運行。像我們前面使用過的main()函數就是一個函數,只不過它比較特
殊,是程序的入口函數,即程序一旦運行,就是從main()函數開始執行的。
但是只有一個main()函數的程序顯然是很初級的,和其他編程語言一樣,Kotlin也允許我們自
由地定義函數,語法規則如下:
fun methodName(param1: Int, param2: Int): Int { return 0
}
下面我來解釋一下上述的語法規則,首先fun(function 的簡寫)是定義函數的關鍵字,無論你
定義什么函數,都一定要使用fun來聲明。
緊跟在fun后面的是函數名,這個就沒有什么要求了,你可以根據自己的喜好起任何名字,但是
良好的編程習慣是函數名最好要有一定的意義,能表達這個函數的作用是什么。
函數名后面緊跟著一對括號,里面可以聲明該函數接收什么參數,參數的數量可以是任意多
個,例如上述示例就表示該函數接收兩個Int類型的參數。參數的聲明格式是“參數名: 參數類
型”,其中參數名也是可以隨便定義的,這一點和函數名類似。如果不想接收任何參數,那么寫
一對空括號就可以了。
參數括號后面的那部分是可選的,用于聲明該函數會返回什么類型的數據,上述示例就表示該
函數會返回一個Int類型的數據。如果你的函數不需要返回任何數據,這部分可以直接不寫。
最后兩個大括號之間的內容就是函數體了,我們可以在這里編寫一個函數的具體邏輯。由于上
述示例中聲明了該函數會返回一個Int類型的數據,因此在函數體中我們簡單地返回了一個0。
這就是定義一個函數最標準的方式了,雖然Kotlin中還有許多其他修飾函數的關鍵字,但是只要
掌握了上述函數定義規則,你就已經能應對80% 以上的編程場景了,至于其他的關鍵字,我們
會在后面慢慢學習。
接下來我們嘗試按照上述定義函數的語法規則來定義一個有意義的函數,如下所示:
fun largerNumber(num1: Int, num2: Int): Int { return max(num1, num2)
}
這里定義了一個名叫largerNumber()的函數,該函數的作用很簡單,接收兩個整型參數,然
后總是返回兩個參數中更大的那個數。
注意,上述代碼中使用了一個max()函數,這是Kotlin提供的一個內置函數,它的作用就是返回
兩個參數中更大的那個數,因此我們的largerNumber()函數其實就是對max()函數做了一層
封裝而已。
現在你可以開始在LearnKotlin 文件中實現largerNumber()這個函數了,當你輸入“max” 這
個單詞時,Android Studio 會自動彈出如圖
Android Studio 擁有非常智能的代碼提示和補全功能,通常你只需要鍵入部分代碼,它就能自
動預測你想要編寫的內容,并給出相應的提示列表。我們可以通過上下鍵在提示列表中移動,
然后按下“Enter” 鍵,Android Studio 就會自動幫我們進行代碼補全了。
這里我非常建議你經常使用Android Studio 的代碼補全功能,可能有些人覺得全部手敲更有成
就感,但是我要提醒一句,使用代碼補全功能后,Android Studio 不僅會幫我們補全代碼,還
會幫我們自動導包,這一點是很重要的。比如說上述的max()函數,如果你全部手敲出來,那
么這個函數一定會提示一個紅色的錯誤,如圖
max()函數提示錯誤
出現這個錯誤的原因是你沒有導入max()函數的包。當然,導包的方法也有很多種,你將光標
移動到這個紅色的錯誤上面就能看到導包的快捷鍵提示,但是最好的做法就是使用Android
Studio 的代碼補全功能,這樣導包工作就自動完成了。
現在我們使用代碼補全功能再來編寫一次max()函數,你會發現LearnKotlin 文件的頭部自動導
入了一個max()函數的包,并且不會再有錯誤提示了,如圖