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()
函式。
- 在瀏覽器中開啟 Kotlin Playground,將內容改為以下程式碼。
fun main() {
println("Happy Birthday, Rover!")
println("You are now 5 years old!")
}
- 在
main()
函式之後,定義名為birthdayGreeting()
的新函式。宣告這個函式的語法和main()
函式相同。
fun main() {
println("Happy Birthday, Rover!")
println("You are now 5 years old!")
}
fun birthdayGreeting() {
}
- 將
main()
中的兩個println()
陳述式移到birthdayGreeting()
函式的大括號內。
fun main() {
}
fun birthdayGreeting() {
println("Happy Birthday, Rover!")
println("You are now 5 years old!")
}
- 在
main()
函式中呼叫birthdayGreeting()
函式。完成的程式碼應如下所示:
fun main() {
birthdayGreeting()
}
fun birthdayGreeting() {
println("Happy Birthday, Rover!")
println("You are now 5 years old!")
}
- 執行程式碼。您應該會看到下列輸出結果:
Happy Birthday, Rover! You are now 5 years old!
3. 透過函式傳回值
在較複雜的應用程式中,函式不只能輸出文字。
Kotlin 函式可產生稱為「回傳值」的資料,這類資料會儲存在變數中,方便在程式碼的其他部分使用。
定義函式時,您可以指定函式要傳回的資料類型。指定傳回類型的方法是在括弧後方加上冒號 (:
)、單一空格,以及類型名稱 (Int
、String
等),然後在傳回類型和左大括號間加上一個空格。在函式主體中的所有陳述式後方,請使用回傳敘述指定要讓函式傳回的值。回傳敘述的開頭為關鍵字 return
,後面接上您希望函式輸出結果時傳回的值,例如變數。
使用傳回類型宣告函式的語法如下所示。
Unit
類型
如未指定傳回類型,則傳回類型會預設為 Unit
。Unit
表示函式不會傳回值。Unit
等同其他語言的 void 傳回類型,例如 Java 和 C 語言的 void
、Swift 語言的 Void
/空元組 ()
、Python 語言的 None
等等。若函式不會傳回值,就會默認傳回 Unit
。如要觀察此現象,可以修改程式碼,讓程式碼傳回 Unit
。
- 在
birthdayGreeting()
的函式宣告中,於右括弧後方加上冒號,然後將傳回類型指定為Unit
。
fun main() {
birthdayGreeting()
}
fun birthdayGreeting(): Unit {
println("Happy Birthday, Rover!")
println("You are now 5 years old!")
}
- 執行程式碼,看看是否一切都還能正常運作。
Happy Birthday, Rover! You are now 5 years old!
您可以選擇在 Kotlin 中指定 Unit
傳回類型。如果函式傳回 Unit
或是不會傳回任何值,您就不必為此編寫回傳敘述。
透過 birthdayGreeting()
傳回 String
為說明函式如何傳回值,請修改 birthdayGreeting()
函式,讓該函式傳回字串,而不是直接輸出結果。
- 將
Unit
傳回類型替換為String
。
fun birthdayGreeting(): String {
println("Happy Birthday, Rover!")
println("You are now 5 years old!")
}
- 執行程式碼。您會收到錯誤訊息。如果您宣告函式的傳回類型 (例如
String
),該函式就必須包含return
陳述式。
A 'return' expression required in a function with a block body ('{...}')
- 一個函式只能傳回一個字串,無法傳回兩個。請使用關鍵字
val
,將println()
陳述式替代為nameGreeting
和ageGreeting,
這兩個變數。由於您從birthdayGreeting()
移除了對println()
的呼叫,因此呼叫birthdayGreeting()
時不會輸出任何值。
fun birthdayGreeting(): String {
val nameGreeting = "Happy Birthday, Rover!"
val ageGreeting = "You are now 5 years old!"
}
- 使用在先前的程式碼研究室中學到的字串格式語法新增
return
陳述式,從含有兩個祝賀詞的函式傳回字串。
您還需使用 \n
逸出字元,為祝賀詞設定分行。這就像您在先前程式碼研究室中學到的 \"
逸出字元一樣,\n
字元會取代為換行字元,讓兩句祝賀詞分別顯示在不同的行。
fun birthdayGreeting(): String {
val nameGreeting = "Happy Birthday, Rover!"
val ageGreeting = "You are now 5 years old!"
return "$nameGreeting\n$ageGreeting"
}
main()
中的birthdayGreeting()
會傳回值,因此您可以將結果儲存在字串變數中。請使用val
宣告greeting
變數,儲存呼叫birthdayGreeting()
的結果。
fun main() {
val greeting = birthdayGreeting()
}
- 在
main()
中呼叫println()
,輸出greeting
字串。main()
函式現在應如下所示。
fun main() {
val greeting = birthdayGreeting()
println(greeting)
}
- 執行程式碼,看看結果是否與之前一樣。您可以透過傳回值的方式將結果儲存在變數中,但如果在
println()
函式中呼叫birthdayGreeting()
函式,會產生什麼樣的結果呢?
Happy Birthday, Rover! You are now 5 years old!
- 移除變數,然後將呼叫
birthdayGreeting()
函式的結果傳遞至println()
函式,如下所示:
fun main() {
println(birthdayGreeting())
}
- 執行程式碼,看看輸出結果。您可以看到,呼叫
birthdayGreeting()
所傳回的值會直接傳遞至println()
。
Happy Birthday, Rover! You are now 5 years old!
4. 在 birthdayGreeting() 函式中新增參數
如您所見,呼叫 println()
時,您可以在括弧中加入字串或「將值傳遞」至函式。您也可以對 birthdayGreeting()
函式執行同樣操作。不過,您需要先將「參數」新增至 birthdayGreeting()
。
參數不僅會指定變數名稱,還會指定可傳入並用於函式的資料類型。您必須在函式名稱後方的括弧中宣告參數。
每個參數包含變數名稱和資料類型,並以冒號和空格分隔。有多個參數時,請以半形逗號分隔參數。
目前 birthdayGreeting()
函式只能用來祝賀 Rover。在 birthdayGreeting()
函式中新增參數後,您才能將任意名字傳遞至函式,用來祝賀不同對象。
- 在
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()
函式的任何地方使用參數的值。在先前的程式碼研究室中,您已經學到如何將變數值插入字串。
- 將
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"
}
- 執行程式碼,並觀察錯誤結果。您現在已宣告
name
參數,因此需要在呼叫birthdayGreeting()
時傳入String
。當您呼叫使用參數的函式時,就會將引數傳遞至函式。引數是您傳遞的值,例如"Rover"
。
No value passed for parameter 'name'
- 將
"Rover"
傳入main()
中的birthdayGreeting()
呼叫。
fun main() {
println(birthdayGreeting("Rover"))
}
- 執行程式碼,看看輸出結果。Rover 這個名字是由
name
參數所產生。
Happy Birthday, Rover! You are now 5 years old!
- 由於
birthdayGreeting()
採用參數,因此呼叫該函式時可使用 Rover 以外的名字。在對println()
的呼叫中新增對birthdayGreeting()
的其他呼叫,傳入引數"Rex"
。
println(birthdayGreeting("Rover"))
println(birthdayGreeting("Rex"))
- 再次執行程式碼,然後觀察因傳入
birthdayGreeting()
的引數不同而有差異的輸出結果。
Happy Birthday, Rover! You are now 5 years old! Happy Birthday, Rex! You are now 5 years old!
5. 包含多個參數的函式
您在上一步中新增了參數,用來變更祝賀對象的名字。不過,您也可以為函式定義多個參數,甚至是不同資料類型的參數。在這個部分中,您將修改祝賀詞,根據狗狗的年齡變更祝賀內容。
參數定義以半形逗號分隔。同樣地,當您使用多個參數呼叫函式時,也會以半形逗號分隔傳遞的引數。一起來看看實際的運作方式吧。
- 在
name
參數後方,將Int
類型的age
參數新增至birthdayGreeting()
函式。新的函式宣告應包含兩個參數 (即name
和age
),並以半形逗號分隔,如下所示:
fun birthdayGreeting(name: String, age: Int): String {
val nameGreeting = "Happy Birthday, $name!"
val ageGreeting = "You are now 5 years old!"
return "$nameGreeting\n$ageGreeting"
}
- 新的祝賀詞字串也應使用
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"
}
- 執行函式,看看輸出的錯誤結果:
No value passed for parameter 'age' No value passed for parameter 'age'
- 在
main()
中修改對birthdayGreeting()
函式的兩個呼叫,以便分別為兩隻狗狗傳遞不同的年齡值,也就是根據 Rover 的年齡傳遞5
,並根據 Rex 的年齡傳遞2
。
fun main() {
println(birthdayGreeting("Rover", 5))
println(birthdayGreeting("Rex", 2))
}
- 執行程式碼。現在您已傳入兩個參數的值,因此呼叫函式時,輸出結果應反映兩隻狗的名字和年齡。
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. 具名引數
在先前的範例中,您呼叫函式時不必指定參數名稱 (name
或 age
),但您也可以選擇這麼做。比方說,您可以呼叫具有多個參數的函式,也可以變更傳遞引數的順序,例如將 age
參數放在 name
參數前方。您在呼叫函式時加入的參數名稱,就稱為「具名引數」。接下來請試試在 birthdayGreeting()
函式中使用具名引數。
- 將對 Rex 的呼叫改為使用具名引數,如下列程式碼片段所示。只要加入參數名稱,並在後方依序加上等號和所需的值即可,例如
name = "Rex"
。
println(birthdayGreeting(name = "Rex", age = 2))
- 執行程式碼。您會看到輸出結果並未改變:
Happy Birthday, Rover! You are now 5 years old! Happy Birthday, Rex! You are now 2 years old!
- 重新排序具名引數。例如將
age
具名引數放在name
具名引數之前。
println(birthdayGreeting(age = 2, name = "Rex"))
- 執行程式碼。您會看到輸出結果不變。即使變更了引數順序,同樣的值仍會傳遞至同一參數。
Happy Birthday, Rover! You are now 5 years old! Happy Birthday, Rex! You are now 2 years old!
7. 預設引數
函式參數也可以指定預設引數。假如 Rover 是您最喜愛的狗,或是您希望在大多數情況下以特定引數呼叫函式,就可以指定預設引數。如果函式具有預設引數,您可以在呼叫函式時選擇省略引數,這樣系統就會使用預設引數。
如要新增預設引數,請在參數的資料類型後方加上 =
指派運算子,然後設定所需參數值。修改程式碼即可使用預設引數。
- 在
birthdayGreeting()
函式中,將name
參數設為預設值"Rover"
。
fun birthdayGreeting(name: String = "Rover", age: Int): String {
return "Happy Birthday, $name! You are now $age years old!"
}
- 在
main()
中第一次針對 Rover 呼叫birthdayGreeting()
時,請將age
具名引數設為5
。age
參數定義位於name
後方,因此您需要使用具名引數age
。如未使用具名引數,Kotlin 語言就會假設引數的順序與參數定義的順序相同。使用具名引數可確保 Kotlin 語言預期的age
參數值為Int
。
println(birthdayGreeting(age = 5))
println(birthdayGreeting("Rex", 2))
- 執行程式碼。對
birthdayGreeting()
函式發出的第一個呼叫會輸出「Rover」做為名字的值,這是因為您從未指定名字。對birthdayGreeting()
的第二次呼叫仍會使用Rex
值,也就是您針對name
傳入的值。
Happy Birthday, Rover! You are now 5 years old! Happy Birthday, Rex! You are now 2 years old!
- 從對
birthdayGreeting()
函式發出的第二個呼叫中移除名字。再次強調,由於name
已省略,您需要為年齡使用具名引數。
println(birthdayGreeting(age = 5))
println(birthdayGreeting(age = 2))
- 執行程式碼。您會看到對
birthdayGreeting()
函式的兩個呼叫現在都會輸出「Rover」做為名字,這是因為系統未傳入具名引數。
Happy Birthday, Rover! You are now 5 years old! Happy Birthday, Rover! You are now 2 years old!
8. 結語
恭喜!您已瞭解如何定義及呼叫 Kotlin 語言的函式。
摘要
- 函式以
fun
關鍵字定義,當中包含可重複使用的程式碼片段。 - 使用函式不僅有助於輕鬆維護大型程式,還能避免不必要的重複程式碼。
- 函式傳回的值可儲存在變數中,供後續使用。
- 函式可採用參數,也就是函式主體中可使用的變數。
- 引數是您呼叫函式時所傳遞的值。
- 您可以在呼叫函式時為引數命名。使用具名引數時可重新排列引數的順序,不會影響輸出結果。
- 您可以指定預設引數,呼叫函式時就能省略引數。