Kotlin Tutorial(9)物件與null << 前情
在物件導向程式設計一個主要的特性是封裝(Encapsulation),基礎的工作是把一組資料與功能包裝在一個類別。功能的部份在Kotlin稱為「函式、function」,對Java來說是類別中的「方法、method」。瞭解函式的設計與應用,是學習物件導向技術一個很重要的部份。
宣告與呼叫函式
設計應用程式的時候,通常會有一些經常需要執行的工作,這些執行工作的程式碼,會在不同的地方執行,所以應該要宣告一個函式包裝這些程式碼。下面是Kotlin宣告函式的語法:
fun 函式名稱(參數名稱:參數型態,...): 回傳值型態 {
...
return ...
}
Kotlin的函式不一定要宣告在某個類別裡面,宣告函式的時候,依照提供的功能決定每一個函式下列三個部份:
- 名稱:根據功能自己取一個函式名稱
- 參數:執行函式的功能,需要由呼叫的地方提供資料才可以正常運作
- 回傳值型態:執行函式的功能以後,函式回傳結果的資料型態
例如下面這個呼叫後會在畫面上顯示訊息的函式,執行的工作非常簡單,所以不需要參數與回傳值:
// 宣告沒有參數、沒有回傳值的函式
fun hello(): Unit {
println("Hello! Kotlin!")
}
// 宣告沒有回傳值的函式,可以省略「:Unit」
fun hello01() {
println("Hello! Kotlin!")
}
fun main(args: Array<String>) {
hello()
// 顯示: Hello! Kotlin!
hello01()
// 顯示: Hello! Kotlin!
}
如果函式在執行後需要回傳資料,必須宣告回傳資料的型態,在函式裡面的程式碼,使用「return」回傳資料:
// 宣告沒有參數、回傳值為String的函式
fun getGreeting(): String {
return "Hello! Kotlin!"
}
fun main(args: Array<String>) {
println( getGreeting() )
// 顯示: Hello! Kotlin!
val greet = getGreeting()
println(greet)
// 顯示: Hello! Kotlin!
}
單一運算式函式
如果一個函式執行的工作比較簡單一些,只有一行回傳結果的敘述,也就是return敘述,可以使用下面的單一運算式函式(Single-Expression functions)的宣告語法:
fun 函式名稱(參數名稱:參數型態=預設值,...) = 運算式
下面的程式片段示範單一運算式函式的宣告與使用:
// 如果函式的程式碼只有一行return敘述
// 可以使用單一運算式函式(Single-Expression functions)的寫法
fun getHello01(): String = "Hello! Kotlin!"
// 單一運算式函式可以省略函式回傳值型態的宣告
fun getHello02() = "Hello! Kotlin!"
fun main(args: Array<String>) {
println( getHello01() )
// 顯示: Hello! Kotlin!
println( getHello02() )
// 顯示: Hello! Kotlin!
}
參數
根據函式的工作需求,可能需要在呼叫的時候,提供資料給函式使用,這些資料通常稱為參數(Parameters)。如果函式需要參數的宣告,在函式名稱後面的左右括號裡面,定義參數的名稱與資料型態,如果需要一個以上的參數,每一個參數之間使用逗號(,)隔開。
下面的程式片段示範參數的宣告與使用:
// 宣告接收一個String參數、沒有回傳值的函式
fun sayHello(name: String) {
println("Hello! $name!")
}
// 宣告接收三個參數、沒有回傳值的函式
fun person01(name: String, age: Int, married: Boolean) {
println("Name: $name, Age: $age, Married: ${if (married) "Yes" else "No"}")
}
fun main(args: Array<String>) {
// 依照函式參數的定義,在呼叫的時候提供一個字串資料給函式使用
sayHello("Simon")
// 顯示: Hello! Simon!
// 呼叫的時候,依照函式參數的宣告,依序提供資料給函式使用
person01("Simon", 35, true)
// 顯示: Name: Simon, Age: 35, Married: Yes
}
函式多載
在設計應用程式時候,有時候會宣告下面這樣的函式:
// 接收兩個整數參數,傳回比較大的數字
fun maxInt(x: Int, y: Int) = if (x > y) x else y
// 接收兩個小數參數,傳回比較大的數字
fun maxDouble(x: Double, y: Double) = if (x > y) x else y
上面的範例為兩個函式依照執行的工作,定義名稱為maxInt與maxDouble,雖然這樣的作法,在使用上很清楚而且沒有問題,不過因為執行的工作同樣為接收兩個參數再傳回比較大的數字,只是參數的型態不一樣而已。這類的函式可以為它們定義同樣的名稱,使用不同的參數個數或型態來區分就可以了。
下面的程式片段示範函式多載的宣告與使用:
// 接收兩個整數參數,傳回比較大的數字
fun max(x: Int, y: Int) = if (x > y) x else y
// 接收兩個小數參數,傳回比較大的數字
fun max(x: Double, y: Double) = if (x > y) x else y
fun main(args: Array<String>) {
// 呼叫函式的時候提供兩個整數,所以會呼叫兩個Int參數的函式
println("max(3, 5) = ${max(3, 5)}")
// 顯示: max(3, 5) = 5
// 呼叫函式的時候提供兩個小數,所以會呼叫兩個Double參數的函式
println("max(3.2, 5.6) = ${max(3.2, 5.6)}")
// 顯示: max(3.2, 5.6) = 5.6
println()
}
參數預設值
使用函式多載可以簡化函式的宣告,也可以使用同樣的名稱呼叫不同的函式,不過每一個不同的參數需求,都需要宣告一個函式。例如下面的範例程式,根據呼叫函式提供的資料,決定個人資訊的預設值:
// 接收姓名、年齡與是否結婚參數並顯示資訊
fun person02(name: String, age: Int, married: Boolean) {
println("Name: $name, Age: $age, Married: ${if (married) "Yes" else "No"}")
}
// 接收姓名與年齡參數,是否結婚預設為否
fun person02(name: String, age: Int) {
println("Name: $name, Age: $age, Married: No")
}
// 接收姓名參數,年齡預設為0,是否結婚預設為否
fun person02(name: String) {
println("Name: $name, Age: 0, Married: No")
}
上面範例程式的需求,可以使用Kotlin提供的參數預設值簡化函式的宣告:
fun 函式名稱(參數名稱:參數型態=預設值,...) {
...
}
下面的程式片段示範參數預設值的宣告與使用:
// 在參數宣告後面設定參數的預設值
fun person03(name: String, age: Int = 0, married: Boolean = false) {
println("Name: $name, Age: $age, Married: ${if (married) "Yes" else "No"}")
}
fun main(args: Array<String>) {
// 呼叫函式的時候,可以省略設定預設值的參數
person03("Mary")
// 顯示: Name: Mary, Age: 0, Married: No
// age與married使用參數設定的預設值
person03("Mary", 3)
// 顯示: Name: Mary, Age: 3, Married: No
// married使用參數設定的預設值
person03("Mary", 32, true)
// 顯示: Name: Mary, Age: 32, Married: Yes
}
參數名稱
如果一個函式有比較多的參數宣告,呼叫的時候,一定要依照參數宣告的順序,提供資料給函式使用。Kotlin提供下面呼叫函式的語法,可以在呼叫函式的時候,依照自己的需求提供參數資料:
下面的程式片段示範在提供參數的時候,使用參數名稱指定參數資料的作法:
fun person03(name: String, age: Int = 0, married: Boolean = false) {
println("Name: $name, Age: $age, Married: ${if (married) "Yes" else "No"}")
}
fun main(args: Array<String>) {
// 呼叫函式的時候,可以使用參數名稱設定傳遞的值
// 就不用依照函式宣告參數的順序傳遞
person03("John", married = true, age = 24)
// 顯示: Name: John, Age: 24, Married: Yes
person03("John", married = true)
// 顯示: Name: John, Age: 0, Married: Yes
}
變動個數參數
有時候函式多載的設計方式,很不適合下面範例程式的需求:
fun averageOverload(a: Int, b: Int) = (a + b) / 2
fun averageOverload(a: Int, b: Int, c: Int) = (a + b + c) / 3
fun averageOverload(a: Int, b: Int, c: Int, d: Int) = (a + b + c + d) / 4
為了提供計算多個整數平均值的功能,使用上面範例程式的設計方式,需要宣告非常多的函式,而且有一種永遠寫不完的感覺,例如計算32個整數平均值的函式。
這類的應用可以使用Kotlin的變動個數參數的宣告,在參數的前面加上「vararg」,可以讓這個參數接收指定型態0到多個資料:
下面的程式片段示範變動個數參數的宣告與使用:
// 使用vararg設定參數可以接收零到多個值
// 呼叫這個函式可以在傳遞零到多個整數
// 參數的資料型態為陣列
fun average(vararg ns: Int): Int {
var total = 0
// 使用迴圈處理陣列的所有元素(合計)
for (n in ns) {
total += n
}
// 傳回平均值
return total / ns.size
}
fun main(args: Array<String>) {
println( average(1, 2, 3) )
println( average(1, 2, 3, 4, 5, 6) )
}
在函式中使用變動個數參數,要遵守下面的規定:
- 一個函式只能有一個設定為vararg的參數
- 如果除了vararg參數外還有其它參數,vararg參數必須宣告在所有參數的最後一個
下面的程式片段示範變動個數參數的宣告與使用:
// 編譯錯誤,一個函式只能有一個設定為vararg的參數
//fun average02(vararg ns: Int, vararg ns2: Int) {
// return 0
//}
// 除了vararg參數外還有其它參數,可是宣告在vararg參數後面
// 雖然這樣的宣告不會發生編譯錯誤...
fun average03(vararg ns: Int, message: String) {
var total = 0
// 使用迴圈處理陣列的所有元素(合計)
for (n in ns) {
total += n
}
val average = total / ns.size
println("$message: $average")
}
// 除了vararg參數外還有其它參數
// vararg參數必須宣告在所有參數的最後一個
fun average04(message: String, vararg ns: Int) {
var total = 0
// 使用迴圈處理陣列的所有元素(合計)
for (n in ns) {
total += n
}
val average = total / ns.size
println("$message: $average")
}
fun main(args: Array<String>) {
// 在呼叫的時候發生編譯錯誤
//println(average03(1,2,3, "Average"))
average04("Average", 1,2,3)
}
下一步
瞭解函式的設計以後,接下來準備為應用程式加入類別,認識物件的建立與使用。
相關的檔案都可以在GitHub瀏覽與下載。
http://github.com/macdidi5/Kotlin-Tutorial
後續 >> Kotlin Tutorial(11)類別與物件
|