本程式碼研究室是程式設計課程 Kotlin 新手上路課程的一部分。使用程式碼研究室逐步完成程式碼課程後,您將能充分發揮本課程的潛能。視您的知識而定,您或許可以略過某些部分。本課程的適用對象為熟悉物件導向語言,且想要學習 Kotlin 的程式設計人員。
簡介
在這個程式碼研究室,您可以建立 Kotlin 程式並瞭解 Kotlin 中的函式,包括參數、篩選器、lambda 和精簡函式的預設值。
本課程並非只建立一個範例應用程式,而是用來建立您的知識,但彼此之間互不相關,因此您可以熟悉自己熟悉的部分。許多產品是透過水族箱主題進行連結。如果您想看完整的水族箱故事,請參考程式設計程式 Kotlin 新手上路課程。
須知事項
- 現代化的物件導向靜態程式設計語言,提供基本知識
- 如何使用至少一種語言來設計課程、方法和例外處理方式
- 如何在 IntelliJ IDEA 中使用 Kotlin's REPL (Read-Eval-Print Loop)
- Kotlin 基本概念,包括類型、運算子和迴圈
本程式碼研究室的適用對象為熟悉物件導向語言並想深入瞭解 Kotlin 的程式設計人員。
課程內容
- 如何在 IntelliJ IDEA 中建立包含
main()
函式與引數的程式 - 如何使用預設值和精簡函式
- 如何套用名單篩選器
- 如何建立基本的 lambda 和順序較高的函式
執行步驟
- 使用 REPL 以嘗試一些程式碼。
- 使用 IntelliJ IDEA 建立基本的 Kotlin 程式。
在這項工作中,您會建立 Kotlin 程式並瞭解 main()
函式,以及如何透過指令列將引數傳送至程式。
您可能會記得先前在先前的程式碼研究室中輸入 REPL 的 printHello()
函式:
fun printHello() {
println ("Hello World")
}
printHello()
⇒ Hello World
您可以先使用 fun
關鍵字定義函式,然後再加上函式名稱。和其他程式設計語言一樣,括號 ()
代表函式引數 (如果有的話)。大括號使用 {}
函式來編寫函式的程式碼。這個函式沒有任何傳回類型,因為這個函式並未傳回任何資料。
步驟 1:建立 Kotlin 檔案
- 開啟 IntelliJ IDEA。
- IntelliJ IDEA 左側的 [Project] (專案) 窗格會顯示您的專案檔案和資料夾清單。在「Hello Kotlin」底下,對 src 資料夾按一下滑鼠右鍵。(您應該已經在先前的程式碼研究室中取得 Hello Kotlin 專案)。
- 選取 [New > Kotlin File / Class]。
- 將「Kind」(種類) 保留為 [File] (檔案),並將檔案命名為 Hello。
- 按一下「OK」(確定)。
src 資料夾中已經有一個名稱為 Hello.kt 的檔案。
步驟 2:加入程式碼並執行程式
- 如同其他程式語言,Kotlin
main()
函式會指定執行作業的進入點。所有指令列引數會以字串陣列的形式傳送。
請將下列程式碼貼到 Hello.kt 檔案中:
fun main(args: Array<String>) {
println("Hello, world!")
}
和先前的 printHello()
函式一樣,此函式沒有 return
陳述式。即使沒有明確指定 Kotlin,所有函式都會傳回部分內容。因此,像此 main()
函式一樣的函式會傳回 kotlin.Unit
類型,這是 Kotlin 的一種,沒有任何值。
- 如要執行程式,請按一下
main()
函式左側的綠色三角形。從選單中選取 [Run 'HelloKt']。 - IntelliJ IDEA 編譯並執行程式。搜尋結果會顯示在底部的記錄窗格中,如下所示。
步驟 3:將引數傳送至 main()
由於您是透過 IntelliJ IDEA 執行程式,而非透過指令列執行程式,因此必須指定一些不同的程式引數。
- 選取 [Run > Edit Configurations]。「Run/Debug Configurations」視窗會隨即開啟。
- 在「Program 改用」欄位中輸入
Kotlin!
。 - 按一下「OK」(確定)。
步驟 4:將程式碼改為使用字串範本
字串範本會在字串中插入變數或運算式,而 $
則代表該字串的一部分為變數或運算式。請大括號 ({}
) 加上運算式 (如果有的話)。
- 在 Hello.kt 中,將問候語改為使用傳送至
args[0]
的第一個引數,而不是"world"
。
fun main(args: Array<String>) {
println("Hello, ${args[0]}")
}
- 執行程式,而輸出結果包含您指定的引數。
⇒ Hello, Kotlin!
在這項工作中,您會瞭解為什麼 Kotlin 中幾乎所有項目都有一個值,以及為什麼該功能很實用。
有些語言則包含陳述式,而這一行程式碼沒有值。在 Kotlin 中,幾乎所有作業都是運算式,而且具有一個值 (即使該值為 kotlin.Unit
)。
- 在 Hello.kt 中,在
main()
中編寫程式碼,將println()
指派給名為isUnit
的變數並加以列印。(println()
不會傳回值,因此會傳回kotlin.Unit
)。
// Will assign kotlin.Unit
val isUnit = println("This is an expression")
println(isUnit)
- 執行程式。第一個
println()
會列印字串"This is an expression"
。第二個println()
會列印第一個println()
陳述式的值,也就是kotlin.Unit
。
⇒ This is an expression kotlin.Unit
- 宣告名為
temperature
的val
並初始化為 10。 - 宣告另一個
val
稱為isHot
,分配if
/else
陳述式的返回值isHot
,如以下代碼所示。因為這是運算式,因此您可以使用if
運算式的值。
val temperature = 10
val isHot = if (temperature > 50) true else false
println(isHot)
⇒ false
- 使用字串範本中的運算式值。加入一些程式碼來檢查溫度,判斷魚是否安全或過熱,然後執行您的計劃。
val temperature = 10
val message = "The water temperature is ${ if (temperature > 50) "too warm" else "OK" }."
println(message)
⇒ The water temperature is OK.
在這項工作中,您可以進一步瞭解 Kotlin 中的函式,並進一步瞭解非常實用的 when
條件運算式。
步驟 1:建立函式
在這個步驟中,您必須彙整自己學到的部分內容,並建立不同類型的函式。您可以將 Hello.kt 的內容替換成新程式碼。
- 編寫名為
feedTheFish()
的函式,呼叫randomDay()
以取得一週的隨機日。使用字串範本列印food
,讓魚類當天可以吃。現在,魚每天每天都吃相同的食物。
fun feedTheFish() {
val day = randomDay()
val food = "pellets"
println ("Today is $day and the fish eat $food")
}
fun main(args: Array<String>) {
feedTheFish()
}
- 撰寫
randomDay()
函式,從陣列挑選隨機日期並傳回。
nextInt()
函式會採用整數限制,將數字從 Random()
限制為 0 到 6,以符合 week
陣列。
fun randomDay() : String {
val week = arrayOf ("Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday", "Sunday")
return week[Random().nextInt(week.size)]
}
Random()
和nextInt()
函式是在java.util.*
中定義。在檔案頂端新增所需的匯入項目:
import java.util.* // required import
- 執行您的程式並檢查輸出結果。
⇒ Today is Tuesday and the fish eat pellets
步驟 2:使用 time 運算式
再延長時間,使用 when
運算式更改程式碼,為不同的日子挑選不同的食物。其他程式設計語言中的 when
陳述式與 switch
類似,但 when
會自動在每個分支的結尾。另外,這個程式碼也可確保程式碼涵蓋所有分支版本。
- 在 Hello.kt 中,新增名為
fishFood()
的函式,將一天視為String
,並以String
傳回當天的魚類食物。使用when()
,這樣每天的魚都會得到一份特定食物。執行程式數次,以查看不同的輸出結果。
fun fishFood (day : String) : String {
var food = ""
when (day) {
"Monday" -> food = "flakes"
"Tuesday" -> food = "pellets"
"Wednesday" -> food = "redworms"
"Thursday" -> food = "granules"
"Friday" -> food = "mosquitoes"
"Saturday" -> food = "lettuce"
"Sunday" -> food = "plankton"
}
return food
}
fun feedTheFish() {
val day = randomDay()
val food = fishFood(day)
println ("Today is $day and the fish eat $food")
}
⇒ Today is Thursday and the fish eat granules
- 使用
else
為when
運算式新增預設分支版本。為了進行測試,為了確保您程式中的預設時間可以套用預設值,請移除Tuesday
和Saturday
分支版本。
使用預設分支版本可確保food
在傳回結果前先取得值,因此不用再初始化。由於程式碼現在只將字串指派給food
,因此您可以使用val
宣告food
,而不是var
。
fun fishFood (day : String) : String {
val food : String
when (day) {
"Monday" -> food = "flakes"
"Wednesday" -> food = "redworms"
"Thursday" -> food = "granules"
"Friday" -> food = "mosquitoes"
"Sunday" -> food = "plankton"
else -> food = "nothing"
}
return food
}
- 每個運算式都有一個值,因此這個程式碼可能更精簡。直接傳回
when
運算式的值,並刪除food
變數。when
運算式的值是滿足條件的最後一個分支版本的值。
fun fishFood (day : String) : String {
return when (day) {
"Monday" -> "flakes"
"Wednesday" -> "redworms"
"Thursday" -> "granules"
"Friday" -> "mosquitoes"
"Sunday" -> "plankton"
else -> "nothing"
}
}
您的程式最終版本看起來像這樣。
import java.util.* // required import
fun randomDay() : String {
val week = arrayOf ("Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday", "Sunday")
return week[Random().nextInt(week.size)]
}
fun fishFood (day : String) : String {
return when (day) {
"Monday" -> "flakes"
"Wednesday" -> "redworms"
"Thursday" -> "granules"
"Friday" -> "mosquitoes"
"Sunday" -> "plankton"
else -> "nothing"
}
}
fun feedTheFish() {
val day = randomDay()
val food = fishFood(day)
println ("Today is $day and the fish eat $food")
}
fun main(args: Array<String>) {
feedTheFish()
}
在這項工作中,您將瞭解函式和方法的預設值。此外,您也可以進一步瞭解精簡函式,藉此讓程式碼更簡潔且易於閱讀,並減少要測試的程式碼路徑數量。密集函式亦稱為「單一運算式函式」。
步驟 1:為參數建立預設值
在 Kotlin 中,您可以透過參數名稱來傳送引數。您也可以指定參數的預設值:如果呼叫者未提供引數,系統就會採用預設值。之後當您編寫方法 (成員函式) 時,您就無需編寫許多相同方法的超載版本。
- 在 Hello.kt 中,撰寫
swim()
函式,其中包含String
參數,以列印魚類的 # 速度。speed
參數的預設值是"fast"
。
fun swim(speed: String = "fast") {
println("swimming $speed")
}
- 從
main()
函式呼叫swim()
函式的三種方法。首先,請使用預設值呼叫函式。接著,請呼叫函式並傳遞speed
參數做為名稱,然後呼叫speed
參數來呼叫函式。
swim() // uses default speed
swim("slow") // positional argument
swim(speed="turtle-like") // named parameter
⇒ swimming fast swimming slow swimming turtle-like
步驟 2:新增必要參數
如果未指定參數的預設值,則一律必須傳送對應的引數。
- 在 Hello.kt 中,撰寫
shouldChangeWater()
函式來接收三個參數:day
、temperature
和dirty
層級。如果需要修改水位,星期四就會傳回true
;如果是星期日、溫度過高或水分過多,該函式就會傳回。星期幾為必要值,但預設溫度為 22,預設負荷度為 20。
請使用不含引數的when
運算式,這類函式在 Kotlin 中會做為一系列的if/else if
檢查。
fun shouldChangeWater (day: String, temperature: Int = 22, dirty: Int = 20): Boolean {
return when {
temperature > 30 -> true
dirty > 30 -> true
day == "Sunday" -> true
else -> false
}
}
- 請撥打
feedTheFish()
與「shouldChangeWater()
」聯絡,並提供該日期。day
參數沒有預設值,您必須指定引數。shouldChangeWater()
的另外兩個參數使用預設值,因此您不需要傳送引數。
fun feedTheFish() {
val day = randomDay()
val food = fishFood(day)
println ("Today is $day and the fish eat $food")
println("Change water: ${shouldChangeWater(day)}")
}
=> Today is Thursday and the fish eat granules Change water: false
步驟 3:建立精簡函式
您在前述步驟中撰寫的 when
運算式,會將大量邏輯封裝成少量的程式碼。如果想要稍微打包但是 Kotlin 的方法是使用精簡函式。
精簡函式 (或一個單一運算式函式) 是 Kotlin 中的常見模式。如果函式傳回單一運算式的結果,您可以在 =
符號後方指定函式的主體,並省略大括號 {}
,並省略 return
。
- 在 Hello.kt 中新增壓縮函式來測試條件。
fun isTooHot(temperature: Int) = temperature > 30
fun isDirty(dirty: Int) = dirty > 30
fun isSunday(day: String) = day == "Sunday"
- 變更
shouldChangeWater()
以呼叫新函式。
fun shouldChangeWater (day: String, temperature: Int = 22, dirty: Int = 20): Boolean {
return when {
isTooHot(temperature) -> true
isDirty(dirty) -> true
isSunday(day) -> true
else -> false
}
}
- 執行程式。
println()
的shouldChangeWater()
輸出結果,應該會和您切換到使用精簡函式時的相同。
預設值
參數的預設值不一定要是值。它可以是另一個函式,如下列部分範例所示:
fun shouldChangeWater (day: String, temperature: Int = 22, dirty: Int = getDirtySensorReading()): Boolean {
...
在這項工作中,您將稍微瞭解 Kotlin 中的篩選器。篩選器可讓您根據一些條件,輕鬆取得部分名單。
步驟 1:建立篩選器
- 在 Hello.kt 中,使用
listOf()
定義頂層的水族箱裝飾清單。您可以取代 Hello.kt 的內容。
val decorations = listOf ("rock", "pagoda", "plastic plant", "alligator", "flowerpot")
- 建立新的
main()
函式,並在當中列印出開頭為「'p'」開頭的裝飾品。篩選器條件的程式碼使用大括號 ({}
),it
代表每個項目在篩選器循環播放時稱為「大括號」。如果運算式傳回true
,就會包含該項目。
fun main() {
println( decorations.filter {it[0] == 'p'})
}
- 執行程式,您會在「Run」(執行) 視窗中看到下列輸出內容:
⇒ [pagoda, plastic plant]
步驟 2:比較 Eager 和延遲延遲篩選器
如果您熟悉其他語言的篩選器,可能會想知道 Kotlin 中的篩選器是「積極」或「延遲」。結果清單是立即建立,還是存取清單?在 Kotlin 中,您可以透過任何方式進行所需的操作。根據預設,filter
是 Eager,而每次使用篩選器時,系統都會建立清單。
如要縮短篩選器的執行時間,您可以使用 Sequence
,這個集合一次只能查看一個項目,包括開頭和結尾。簡單來說,就是延遲篩選器所需的 API。
- 在 Hello.kt 中,變更程式碼,將篩選後的清單指派給名為
eager
的變數,然後進行列印。
fun main() {
val decorations = listOf ("rock", "pagoda", "plastic plant", "alligator", "flowerpot")
// eager, creates a new list
val eager = decorations.filter { it [0] == 'p' }
println("eager: " + eager)
- 在該程式碼下方,使用
Sequence
搭配asSequence()
評估篩選器。將序列指派給名為filtered
的變數,然後進行列印。
// lazy, will wait until asked to evaluate
val filtered = decorations.asSequence().filter { it[0] == 'p' }
println("filtered: " + filtered)
當您將篩選器結果傳回為 Sequence
時,filtered
變數不會存放新清單,而會保有 Sequence
清單元素元素以及篩選器的知識,以套用這些元素。每當您存取 Sequence
中的元素時,系統就會套用篩選器並傳回結果。
- 將序列轉換為具有
toList()
的List
,強制評估序列。列印結果。
// force evaluation of the lazy list
val newList = filtered.toList()
println("new list: " + newList)
- 執行程式並觀察輸出結果。
⇒ eager: [pagoda, plastic plant] filtered: kotlin.sequences.FilteringSequence@386cc1c4 new list: [pagoda, plastic plant]
如要使用 Sequence
和延遲評估功能呈現視覺化內容,請使用 map()
函式。map()
函式會針對序列中的每個元素執行簡單的轉換。
- 使用與上述相同的
decorations
清單時,使用map()
執行沒有任何轉換的轉換作業,然後只傳回傳送的元素。新增println()
,以便在每次存取元素時顯示,並將序列指派給名為lazyMap
的變數。
val lazyMap = decorations.asSequence().map {
println("access: $it")
it
}
- 列印
lazyMap
,使用first()
列印lazyMap
的第一個元素,並列印lazyMap
轉換成List
。
println("lazy: $lazyMap")
println("-----")
println("first: ${lazyMap.first()}")
println("-----")
println("all: ${lazyMap.toList()}")
- 執行程式並觀察輸出內容。列印
lazyMap
只列印Sequence
的參照 - 內部呼叫println()
並未呼叫。列印第一個元素只會存取第一個元素。將Sequence
轉換為List
會存取所有元素。
⇒ lazy: kotlin.sequences.TransformingSequence@5ba23b66 ----- access: rock first: rock ----- access: rock access: pagoda access: plastic plant access: alligator access: flowerpot all: [rock, pagoda, plastic plant, alligator, flowerpot]
- 套用
map
前,請使用原始篩選器建立新的Sequence
。列印結果。
val lazyMap2 = decorations.asSequence().filter {it[0] == 'p'}.map {
println("access: $it")
it
}
println("-----")
println("filtered: ${ lazyMap2.toList() }")
- 執行程式並觀察其他輸出內容。如同取得第一個元素,系統只對存取的元素呼叫內部
println()
。
⇒ ----- access: pagoda access: plastic plant filtered: [pagoda, plastic plant]
在這項工作中,您將介紹 Kotlin 中的 lambda 和更高順序函式。
Lambdas
除了傳統的已命名函式以外,Kotlin 也支援 lambdas。lambda 是用來建立函式的運算式。但您必須宣告沒有名稱的函式,而不是宣告已命名的函式。使用 lambda 運算式可以作為資料傳送,其中一個有用的方式。其他語言的 lambda 稱為匿名函式、函式常值或類似名稱。
高階函式
您只要將 lambda 傳遞至另一個函式,就能建立順序較高的函式。在先前的工作中,您建立了一個名為 filter
的較高順序函式。您已將下列 lambda 運算式傳送給 filter
,做為檢查條件:{it[0] == 'p'}
同樣地,map
是順序更高的函式,您傳遞至該 lambda 則是要套用的轉換。
步驟 1:瞭解 lambdas
- lambdas 與已命名函式一樣具有參數。針對 lambdas,參數 (及其類型,如有必要) 會在函式函式
->
的左側顯示。要執行的程式碼位於函式箭頭的右側。將 lambda 指派給變數後,您就可以將其呼叫為函式。
使用 REPL ([工具] > [Kotlin > Kotlin REPL]),試用這個程式碼:
var dirtyLevel = 20
val waterFilter = { dirty : Int -> dirty / 2}
println(waterFilter(dirtyLevel))
⇒ 10
在這個範例中,lambda 會使用名為 dirty
的 Int
,並傳回 dirty / 2
。(因為篩選功能會移除泥土。)
- Kotlin 的函式類型語法與 lambdas 的語法密切相關。請使用這個語法明確宣告含有函式的變數:
val waterFilter: (Int) -> Int = { dirty -> dirty / 2 }
程式碼的內容如下:
- 建立名為「
waterFilter
」的變數。 waterFilter
可以是任何採用Int
並傳回Int
的函式。- 將 lambda 指派給
waterFilter
。 - lambda 會傳回引數
dirty
除以 2 的值。
請注意,您再也不需要指定 lambda 引數的類型,類型是由類型推論計算得出。
步驟 2:建立順序較高的函式
截至目前為止,lambda 的範例看起來很像功能。lambdas 真正的功用是利用這些技術建立高階函式,其中某個函式的引數是另一個函式。
- 編寫更高的順序函式。這裡是基本範例,這個函式接收兩個引數。第一個引數是整數。第二個引數是一個需要整數並傳回整數的函式。在 REPL 中試用。
fun updateDirty(dirty: Int, operation: (Int) -> Int): Int {
return operation(dirty)
}
程式碼的主體會呼叫做為第二個引數傳送的函式,並傳遞第一個引數。
- 如要呼叫此函式,請傳送整數和函式。
val waterFilter: (Int) -> Int = { dirty -> dirty / 2 }
println(updateDirty(30, waterFilter))
⇒ 15
您傳遞的函式不一定要是 lambda,也可以是一般命名函式。如要指定引數為一般函式,請使用 ::
運算子。這樣一來,Kotlin 就會得知您將函式參照做為引數傳送,而不是嘗試呼叫函式。
- 請將一般已命名函式傳送至
updateDirty()
。
fun increaseDirty( start: Int ) = start + 1
println(updateDirty(15, ::increaseDirty))
⇒ 16
var dirtyLevel = 19;
dirtyLevel = updateDirty(dirtyLevel) { dirtyLevel -> dirtyLevel + 23}
println(dirtyLevel)
⇒ 42
- 如要在 IntelliJ IDEA 中建立 Kotlin 來源檔案,請先建立 Kotlin 專案。
- 如要在 IntelliJ IDEA 中編譯及執行程式,請按一下
main()
函式旁邊的綠色三角形。輸出結果會顯示在下方的記錄視窗中。 - 在 IntelliJ IDEA 中,指定要在 Run > Edit Configurations 中傳送到
main()
函式的指令列引數。 - 在 Kotlin 中,幾乎所有的值都有一個值。您可以使用這項技巧,將
if
或when
的值做為運算式或傳回值,讓程式碼更加簡潔。 - 預設引數會移除多個函式或方法的版本。例如:
fun swim(speed: String = "fast") { ... }
- 精簡函式 (或單一運算式函式) 可讓您的程式碼更清晰易讀。例如:
fun isTooHot(temperature: Int) = temperature > 30
- 您已經瞭解使用 lambda 運算式的篩選器相關基本知識。例如:
val beginsWithP = decorations.filter { it [0] == 'p' }
- lambda 運算式是用來建立未命名函式的運算式。大括號在
{}
大括號之間有定義。 - 在高順序函式中,您將 lambda 運算式等函式傳送至另一個函式。例如:
dirtyLevel = updateDirty(dirtyLevel) { dirtyLevel -> dirtyLevel + 23}
這堂課的內容很多,特別是對新手,之後的課程會再次介紹 lambda 和更高順序的功能。
Kotlin 說明文件
如果想瞭解本課程的任一主題,或您的困難,建議您選擇 https://kotlinlang.org。
Kotlin 教學課程
https://try.kotlinlang.org 網站包含稱為 Kotlin Koans 的豐富的教學課程、網頁式解譯器,以及包含參考範例的完整參考文件。
Udacity 課程
如要查看這個主題的 Udacity 課程,請參閱程式設計人員 Kotlin 新手上路課程。
IntelliJ IDEA
如需 IntelliJ IDEA 說明文件,請前往 JetBrains 網站。
這個部分會列出在代碼研究室中,受老師主導的課程作業的可能學生作業。由老師自行決定要執行下列動作:
- 視需要指派家庭作業。
- 告知學生如何提交家庭作業。
- 批改家庭作業。
老師可視需要使用這些建議,並視情況指派其他合適的家庭作業。
如果您是自行操作本程式碼研究室,歡迎透過這些家庭作業來測試自己的知識。
回答這些問題
第 1 題
如果字串 element
包含在字串中,contains(element: String)
函式會傳回 true
。以下程式碼會輸出何種內容?
val decorations = listOf ("rock", "pagoda", "plastic plant", "alligator", "flowerpot")
println(decorations.filter {it.contains('p')})
▢ [pagoda, plastic, plant]
▢ [pagoda, plastic plant]
▢ [pagoda, plastic plant, flowerpot]
▢ [rock, alligator]
第 2 題
以下函式定義中,何者是必要參數的?fun shouldChangeWater (day: String, temperature: Int = 22, dirty: Int = 20, numDecorations: Int = 0): Boolean {...}
▢ numDecorations
▢ dirty
▢ day
▢ temperature
第 3 題
您可以將一般命名函式 (而非呼叫此結果) 傳遞至其他函式。您要如何把increaseDirty( start: Int ) = start + 1
傳給updateDirty(dirty: Int, operation: (Int) -> Int)
?
▢ updateDirty(15, &increaseDirty())
▢ updateDirty(15, increaseDirty())
▢ updateDirty(15, ("increaseDirty()"))
▢ updateDirty(15, ::increaseDirty)
繼續下一堂課:
如需課程簡介,包括其他程式碼研究室的連結,請參閱程式設計人員 Kotlin 新手課程:歡迎參加這堂課程。