建立及使用 Kotlin 函式

1. 事前準備

在先前的程式碼研究室中,您看到了一個會顯示 Hello, world! 的簡易程式。截至目前為止,您在編寫的程式中使用了以下兩種函式:

  • main() 函式:每個 Kotlin 程式都必須用到的函式,這是程式的進入點,也可以說是起點。
  • println() 函式,可從 main() 呼叫,用來輸出文字。

在這個程式碼研究室中,您將進一步瞭解函式。

函式可讓您將程式碼分成可重複使用的片段,而不是將所有程式碼納入 main()。函式是 Android 應用程式不可或缺的構成要素,因此瞭解如何定義及使用函式是成為 Android 開發人員重要的一步。

必要條件

  • Kotlin 程式設計的基本知識,包括變數與 println()main() 函式。

課程內容

  • 如何定義及呼叫自己的函式。
  • 如何從函式傳回可儲存在變數中的值。
  • 如何使用多個參數定義及呼叫函式。
  • 如何使用具名引數呼叫函式。
  • 如何設定函式參數的預設值。

軟硬體需求

  • 可使用 Kotlin Playground 的網路瀏覽器。

2. 定義及呼叫函式

在深入探索函式前,讓我們先複習一些基本術語。

  • 宣告或定義函式意味著使用 fun 關鍵字,並用大括號括住包含執行工作所需指示的程式碼。
  • 呼叫函式會讓系統執行該函式中的所有程式碼。

到目前為止,您都使用 main() 函式編寫所有程式碼。實際上,main() 函式不會在程式碼中呼叫,而是由 Kotlin 編譯器用做程式碼的起點。main() 函式只會包含您要執行的其他程式碼,例如對 println() 函式的呼叫。

println() 函式屬於 Kotlin 語言的一部分。不過,您也可以定義自己的函式,方便在需要多次呼叫函式時重複使用程式碼。以下列程式為例:

fun main() {
    println("Happy Birthday, Rover!")
    println("You are now 5 years old!")
}

main() 函式包含兩種 println() 陳述式,一個用於祝福 Rover 生日快樂,另一個說明 Rover 的年齡。

雖然您可以使用 Kotlin 語言將所有程式碼放入 main() 函式,但您可能不一定會想這麼做。舉例來說,假如您還想在程式中加入新年賀詞,就也須在主函式中加入對 println() 的呼叫。或者您可能想要多次祝賀 Rover。這時您可以單純複製及貼上程式碼,也可以另外建立生日祝賀詞專用的函式。您接下來將採用第二種做法。依特定工作建立專屬函式有多種好處,包括:

  • 可重複使用程式碼:您不必多次複製及貼上所需程式碼,只要視需要呼叫函式即可。
  • 可讀性佳:確保函式只執行一項特定工作,不但有助其他開發人員和團隊成員準確掌握程式碼片段的用途,也方便您日後辨識。

定義函式的語法如下圖所示。

定義函式的語法

函式定義的開頭為 fun 關鍵字,後面接著函式名稱、一組左右括號和一組左右大括號。程式碼會放在大括號內,會在呼叫函式時執行。

您將建立新的函式,用來將兩個 println() 陳述式移出 main() 函式。

  1. 在瀏覽器中開啟 Kotlin Playground,將內容改為以下程式碼。
fun main() {
    println("Happy Birthday, Rover!")
    println("You are now 5 years old!")
}
  1. main() 函式之後,定義名為 birthdayGreeting() 的新函式。宣告這個函式的語法和 main() 函式相同。
fun main() {
    println("Happy Birthday, Rover!")
    println("You are now 5 years old!")
}

fun birthdayGreeting() {

}
  1. main() 中的兩個 println() 陳述式移到 birthdayGreeting() 函式的大括號內。
fun main() {

}

fun birthdayGreeting() {
    println("Happy Birthday, Rover!")
    println("You are now 5 years old!")
}
  1. main() 函式中呼叫 birthdayGreeting() 函式。完成的程式碼應如下所示:
fun main() {
    birthdayGreeting()
}

fun birthdayGreeting() {
    println("Happy Birthday, Rover!")
    println("You are now 5 years old!")
}
  1. 執行程式碼。您應該會看到下列輸出結果:
Happy Birthday, Rover!
You are now 5 years old!

3. 透過函式傳回值

在較複雜的應用程式中,函式不只能輸出文字。

Kotlin 函式可產生稱為「回傳值」的資料,這類資料會儲存在變數中,方便在程式碼的其他部分使用。

定義函式時,您可以指定函式要傳回的資料類型。指定傳回類型的方法是在括弧後方加上冒號 (:)、單一空格,以及類型名稱 (IntString 等),然後在傳回類型和左大括號間加上一個空格。在函式主體中的所有陳述式後方,請使用回傳敘述指定要讓函式傳回的值。回傳敘述的開頭為關鍵字 return,後面接上您希望函式輸出結果時傳回的值,例如變數。

使用傳回類型宣告函式的語法如下所示。

使用傳回類型宣告函式的語法

Unit 類型

如未指定傳回類型,則傳回類型會預設為 UnitUnit 表示函式不會傳回值。Unit 等同其他語言的 void 傳回類型,例如 Java 和 C 語言的 void、Swift 語言的 Void/空元組 ()、Python 語言的 None 等等。若函式不會傳回值,就會默認傳回 Unit。如要觀察此現象,可以修改程式碼,讓程式碼傳回 Unit

  1. birthdayGreeting() 的函式宣告中,於右括弧後方加上冒號,然後將傳回類型指定為 Unit
fun main() {
    birthdayGreeting()
}

fun birthdayGreeting(): Unit {
    println("Happy Birthday, Rover!")
    println("You are now 5 years old!")
}
  1. 執行程式碼,看看是否一切都還能正常運作。
Happy Birthday, Rover!
You are now 5 years old!

您可以選擇在 Kotlin 中指定 Unit 傳回類型。如果函式傳回 Unit 或是不會傳回任何值,您就不必為此編寫回傳敘述。

透過 birthdayGreeting() 傳回 String

為說明函式如何傳回值,請修改 birthdayGreeting() 函式,讓該函式傳回字串,而不是直接輸出結果。

  1. Unit 傳回類型替換為 String
fun birthdayGreeting(): String {
    println("Happy Birthday, Rover!")
    println("You are now 5 years old!")
}
  1. 執行程式碼。您會收到錯誤訊息。如果您宣告函式的傳回類型 (例如 String),該函式就必須包含 return 陳述式。
A 'return' expression required in a function with a block body ('{...}')
  1. 一個函式只能傳回一個字串,無法傳回兩個。請使用關鍵字 val,將 println() 陳述式替代為 nameGreetingageGreeting, 這兩個變數。由於您從 birthdayGreeting() 移除了對 println() 的呼叫,因此呼叫 birthdayGreeting() 時不會輸出任何值。
fun birthdayGreeting(): String {
    val nameGreeting = "Happy Birthday, Rover!"
    val ageGreeting = "You are now 5 years old!"
}
  1. 使用在先前的程式碼研究室中學到的字串格式語法新增 return 陳述式,從含有兩個祝賀詞的函式傳回字串。

您還需使用 \n 逸出字元,為祝賀詞設定分行。這就像您在先前程式碼研究室中學到的 \" 逸出字元一樣,\n 字元會取代為換行字元,讓兩句祝賀詞分別顯示在不同的行。

fun birthdayGreeting(): String {
    val nameGreeting = "Happy Birthday, Rover!"
    val ageGreeting = "You are now 5 years old!"
    return "$nameGreeting\n$ageGreeting"
}
  1. main() 中的 birthdayGreeting() 會傳回值,因此您可以將結果儲存在字串變數中。請使用 val 宣告 greeting 變數,儲存呼叫 birthdayGreeting() 的結果。
fun main() {
    val greeting = birthdayGreeting()
}
  1. main() 中呼叫 println(),輸出 greeting 字串。main() 函式現在應如下所示。
fun main() {
    val greeting = birthdayGreeting()
    println(greeting)
}
  1. 執行程式碼,看看結果是否與之前一樣。您可以透過傳回值的方式將結果儲存在變數中,但如果在 println() 函式中呼叫 birthdayGreeting() 函式,會產生什麼樣的結果呢?
Happy Birthday, Rover!
You are now 5 years old!
  1. 移除變數,然後將呼叫 birthdayGreeting() 函式的結果傳遞至 println() 函式,如下所示:
fun main() {
    println(birthdayGreeting())
}
  1. 執行程式碼,看看輸出結果。您可以看到,呼叫 birthdayGreeting() 所傳回的值會直接傳遞至 println()
Happy Birthday, Rover!
You are now 5 years old!

4. 在 birthdayGreeting() 函式中新增參數

如您所見,呼叫 println() 時,您可以在括弧中加入字串或「將值傳遞」至函式。您也可以對 birthdayGreeting() 函式執行同樣操作。不過,您需要先將「參數」新增至 birthdayGreeting()

參數不僅會指定變數名稱,還會指定可傳入並用於函式的資料類型。您必須在函式名稱後方的括弧中宣告參數。

使用參數和傳回類型宣告函式的語法

每個參數包含變數名稱和資料類型,並以冒號和空格分隔。有多個參數時,請以半形逗號分隔參數。

目前 birthdayGreeting() 函式只能用來祝賀 Rover。在 birthdayGreeting() 函式中新增參數後,您才能將任意名字傳遞至函式,用來祝賀不同對象。

  1. birthdayGreeting() 函式的括弧中,使用語法 name: String 新增 String 類型的 name 參數。
fun birthdayGreeting(name: String): String {
    val nameGreeting = "Happy Birthday, Rover!"
    val ageGreeting = "You are now 5 years old!"
    return "$nameGreeting\n$ageGreeting"
}

上一步驟中所定義參數的運作方式類似於透過 val 關鍵字宣告的變數。您可以在 birthdayGreeting() 函式的任何地方使用參數的值。在先前的程式碼研究室中,您已經學到如何將變數值插入字串。

  1. nameGreeting 字串中的 Rover 改成 $ 符號,並在後方接上 name 參數。
fun birthdayGreeting(name: String): String {
    val nameGreeting = "Happy Birthday, $name!"
    val ageGreeting = "You are now 5 years old!"
    return "$nameGreeting\n$ageGreeting"
}
  1. 執行程式碼,並觀察錯誤結果。您現在已宣告 name 參數,因此需要在呼叫 birthdayGreeting() 時傳入 String。當您呼叫使用參數的函式時,就會將引數傳遞至函式。引數是您傳遞的值,例如 "Rover"
No value passed for parameter 'name'
  1. "Rover" 傳入 main() 中的 birthdayGreeting() 呼叫。
fun main() {
    println(birthdayGreeting("Rover"))
}
  1. 執行程式碼,看看輸出結果。Rover 這個名字是由 name 參數所產生。
Happy Birthday, Rover!
You are now 5 years old!
  1. 由於 birthdayGreeting() 採用參數,因此呼叫該函式時可使用 Rover 以外的名字。在對 println() 的呼叫中新增對 birthdayGreeting() 的其他呼叫,傳入引數 "Rex"
println(birthdayGreeting("Rover"))
println(birthdayGreeting("Rex"))
  1. 再次執行程式碼,然後觀察因傳入 birthdayGreeting() 的引數不同而有差異的輸出結果。
Happy Birthday, Rover!
You are now 5 years old!
Happy Birthday, Rex!
You are now 5 years old!

5. 包含多個參數的函式

您在上一步中新增了參數,用來變更祝賀對象的名字。不過,您也可以為函式定義多個參數,甚至是不同資料類型的參數。在這個部分中,您將修改祝賀詞,根據狗狗的年齡變更祝賀內容。

包含多個參數的函式

參數定義以半形逗號分隔。同樣地,當您使用多個參數呼叫函式時,也會以半形逗號分隔傳遞的引數。一起來看看實際的運作方式吧。

  1. name 參數後方,將 Int 類型的 age 參數新增至 birthdayGreeting() 函式。新的函式宣告應包含兩個參數 (即 nameage),並以半形逗號分隔,如下所示:
fun birthdayGreeting(name: String, age: Int): String {
    val nameGreeting = "Happy Birthday, $name!"
    val ageGreeting = "You are now 5 years old!"
    return "$nameGreeting\n$ageGreeting"
}
  1. 新的祝賀詞字串也應使用 age 參數。更新 birthdayGreeting() 函式,使用 ageGreeting 字串中的 age 參數值。
fun birthdayGreeting(name: String, age: Int): String {
    val nameGreeting = "Happy Birthday, $name!"
    val ageGreeting = "You are now $age years old!"
    return "$nameGreeting\n$ageGreeting"
}
  1. 執行函式,看看輸出的錯誤結果:
No value passed for parameter 'age'
No value passed for parameter 'age'
  1. main() 中修改對 birthdayGreeting() 函式的兩個呼叫,以便分別為兩隻狗狗傳遞不同的年齡值,也就是根據 Rover 的年齡傳遞 5,並根據 Rex 的年齡傳遞 2
fun main() {
    println(birthdayGreeting("Rover", 5))
    println(birthdayGreeting("Rex", 2))
}
  1. 執行程式碼。現在您已傳入兩個參數的值,因此呼叫函式時,輸出結果應反映兩隻狗的名字和年齡。
Happy Birthday, Rover!
You are now 5 years old!
Happy Birthday, Rex!
You are now 2 years old!

函式簽章

到目前為止,您已瞭解如何定義函式名稱、輸入項目 (參數) 和輸出結果。包含輸入項目 (參數) 的函式名稱統稱為「函式簽章」。函式簽章包含傳回類型前面的所有項目,如以下程式碼片段所示。

fun birthdayGreeting(name: String, age: Int)

以半形逗號分隔的參數有時稱為「參數清單」。

您通常會在其他開發人員撰寫的程式碼說明文件中看到上述字詞。函式簽章可指出函式名稱以及可傳入的資料類型。

您已瞭解許多定義函式的新語法,不妨查看下圖回顧函式語法的重點。

函式語法回顧圖表

6. 具名引數

在先前的範例中,您呼叫函式時不必指定參數名稱 (nameage),但您也可以選擇這麼做。比方說,您可以呼叫具有多個參數的函式,也可以變更傳遞引數的順序,例如將 age 參數放在 name 參數前方。您在呼叫函式時加入的參數名稱,就稱為「具名引數。接下來請試試在 birthdayGreeting() 函式中使用具名引數。

  1. 將對 Rex 的呼叫改為使用具名引數,如下列程式碼片段所示。只要加入參數名稱,並在後方依序加上等號和所需的值即可,例如 name = "Rex"
println(birthdayGreeting(name = "Rex", age = 2))
  1. 執行程式碼。您會看到輸出結果並未改變:
Happy Birthday, Rover!
You are now 5 years old!
Happy Birthday, Rex!
You are now 2 years old!
  1. 重新排序具名引數。例如將 age 具名引數放在 name 具名引數之前。
println(birthdayGreeting(age = 2, name = "Rex"))
  1. 執行程式碼。您會看到輸出結果不變。即使變更了引數順序,同樣的值仍會傳遞至同一參數。
Happy Birthday, Rover!
You are now 5 years old!
Happy Birthday, Rex!
You are now 2 years old!

7. 預設引數

函式參數也可以指定預設引數。假如 Rover 是您最喜愛的狗,或是您希望在大多數情況下以特定引數呼叫函式,就可以指定預設引數。如果函式具有預設引數,您可以在呼叫函式時選擇省略引數,這樣系統就會使用預設引數。

如要新增預設引數,請在參數的資料類型後方加上 = 指派運算子,然後設定所需參數值。修改程式碼即可使用預設引數。

  1. birthdayGreeting() 函式中,將 name 參數設為預設值 "Rover"
fun birthdayGreeting(name: String = "Rover", age: Int): String {
    return "Happy Birthday, $name! You are now $age years old!"
}
  1. main() 中第一次針對 Rover 呼叫 birthdayGreeting() 時,請將 age 具名引數設為 5age 參數定義位於 name 後方,因此您需要使用具名引數 age。如未使用具名引數,Kotlin 語言就會假設引數的順序與參數定義的順序相同。使用具名引數可確保 Kotlin 語言預期的 age 參數值為 Int
println(birthdayGreeting(age = 5))
println(birthdayGreeting("Rex", 2))
  1. 執行程式碼。對 birthdayGreeting() 函式發出的第一個呼叫會輸出「Rover」做為名字的值,這是因為您從未指定名字。對 birthdayGreeting() 的第二次呼叫仍會使用 Rex 值,也就是您針對 name 傳入的值。
Happy Birthday, Rover! You are now 5 years old!
Happy Birthday, Rex! You are now 2 years old!
  1. 從對 birthdayGreeting() 函式發出的第二個呼叫中移除名字。再次強調,由於 name 已省略,您需要為年齡使用具名引數。
println(birthdayGreeting(age = 5))
println(birthdayGreeting(age = 2))
  1. 執行程式碼。您會看到對 birthdayGreeting() 函式的兩個呼叫現在都會輸出「Rover」做為名字,這是因為系統未傳入具名引數。
Happy Birthday, Rover! You are now 5 years old!
Happy Birthday, Rover! You are now 2 years old!

8. 結語

恭喜!您已瞭解如何定義及呼叫 Kotlin 語言的函式。

摘要

  • 函式以 fun 關鍵字定義,當中包含可重複使用的程式碼片段。
  • 使用函式不僅有助於輕鬆維護大型程式,還能避免不必要的重複程式碼。
  • 函式傳回的值可儲存在變數中,供後續使用。
  • 函式可採用參數,也就是函式主體中可使用的變數。
  • 引數是您呼叫函式時所傳遞的值。
  • 您可以在呼叫函式時為引數命名。使用具名引數時可重新排列引數的順序,不會影響輸出結果。
  • 您可以指定預設引數,呼叫函式時就能省略引數。