程式設計人員的 Kotlin 新手上路課程 2:Kotlin 基本概念

這個程式碼研究室是「程式設計人員的 Kotlin 新手上路課程」的一部分。如果您按部就班完成程式碼研究室,就能充分體驗到本課程的價值。視您的知識多寡而定,您或許能略過某些部分。本課程適用於熟悉物件導向語言,且想學習 Kotlin 的程式設計師。

簡介

在本程式碼研究室中,您將學習 Kotlin 程式設計語言的基本概念,包括資料型別、運算子、變數、控制結構,以及可為空值和不可為空值的變數。本課程適用於熟悉物件導向語言,且想學習 Kotlin 的程式設計師。

本課程的設計目標是協助您累積知識,但各單元之間彼此半獨立,因此您可以略過熟悉的部分,不必建構單一範例應用程式。為將這些範例連結在一起,許多範例都使用水族館主題。如要查看完整的魚缸故事,請參閱 Udacity 的程式設計人員 Kotlin 新手上路課程

必備知識

  • 如何在 IntelliJ IDEA 中建立專案
  • 如何在 IntelliJ IDEA 中開啟及執行 Kotlin 的 REPL (Read-Eval-Print Loop) 程式碼 (依序點選「Tools」>「Kotlin」>「Kotlin REPL」)

課程內容

  • 如何使用 Kotlin 資料類型、運算子和變數
  • 如何使用布林值和條件
  • 可為空值和不可為空值的變數之間的差異
  • Kotlin 中的陣列、清單和迴圈運作方式

學習內容

  • 使用 Kotlin REPL 學習 Kotlin 基礎知識

在這項工作中,您將瞭解 Kotlin 程式設計語言中的運算子和型別。

步驟 1:探索數值運算子

  1. 開啟 IntelliJ IDEA (如果尚未開啟)。
  2. 如要開啟 Kotlin REPL,請依序選取「Tools」 >「Kotlin」 >「Kotlin REPL」

與其他語言一樣,Kotlin 使用 +-*/ 分別代表加、減、乘和除。Kotlin 也支援不同的數字類型,例如 IntLongDoubleFloat

  1. 在 REPL 中輸入下列運算式。如要查看結果,請在輸入每個運算式後按下 Control+Enter 鍵 (在 Mac 上則為 Command+Enter 鍵)。
1+1
⇒ res8: kotlin.Int = 2

53-3
⇒ res9: kotlin.Int = 50

50/10
⇒ res10: kotlin.Int = 5

1.0/2.0
⇒ res11: kotlin.Double = 0.5

2.0*3.5
⇒ res12: kotlin.Double = 7.0

請注意,運算結果會保留運算元的型別,因此 1/2 = 0,但 1.0/2.0 = 0.5。

  1. 請嘗試使用整數和十進位數字的不同組合來建立運算式。
6*50
⇒ res13: kotlin.Int = 300

6.0*50.0
⇒ res14: kotlin.Double = 300.0

6.0*50
⇒ res15: kotlin.Double = 300.0
  1. 呼叫數字的一些方法。Kotlin 會將數字保留為基本型別,但您可以在數字上呼叫方法,就像數字是物件一樣。
2.times(3)
⇒ res5: kotlin.Int = 6

3.5.plus(4)
⇒ res8: kotlin.Double = 7.5

2.4.div(2)
⇒ res9: kotlin.Double = 1.2

步驟 2:練習使用型別

Kotlin 不會在數字型別之間隱含轉換,因此您無法將 short 值直接指派給 long 變數,或將 Byte 指派給 Int。這是因為程式中常見的錯誤來源是隱含數字轉換。您隨時可以透過轉換類型指派不同類型的值。

  1. 如要查看一些可能的轉換,請在 REPL 中定義 Int 類型的變數。
val i: Int = 6
  1. 建立新變數,然後輸入上述變數名稱,後面加上 .to
val b1 = i.to

IntelliJ IDEA 會顯示可能的完成項目清單。這項自動完成功能適用於任何型別的變數和物件。

  1. 從清單中選取 toByte(),然後列印變數。
val b1 = i.toByte()
println(b1)
⇒ 6
  1. 為不同類型的變數指派 Byte 值。
val b2: Byte = 1 // OK, literals are checked statically
println(b2)
⇒ 1

val i1: Int = b2
⇒ error: type mismatch: inferred type is Byte but Int was expected

val i2: String = b2
⇒ error: type mismatch: inferred type is Byte but String was expected

val i3: Double = b2
⇒ error: type mismatch: inferred type is Byte but Double was expected
  1. 如果作業傳回錯誤,請改為投放到電視。
val i4: Int = b2.toInt() // OK!
println(i4)
⇒ 1

val i5: String = b2.toString()
println(i5)
⇒ 1

val i6: Double = b2.toDouble()
println(i6)
⇒ 1.0
  1. 為了讓長數字常數更易讀,Kotlin 允許您在數字中加入底線,方便您閱讀。請嘗試輸入其他數值常數。
val oneMillion = 1_000_000
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010

步驟 3:瞭解變數類型的值

Kotlin 支援兩種類型的變數:可變更和不可變更。使用 val 時,您可以指派值一次。如果您嘗試重新指派其他值,就會收到錯誤訊息。使用 var 時,您可以指派值,之後也能透過程式變更該值。

  1. 使用 valvar 定義變數,然後為變數指派新值。
var fish = 1
fish = 2
val aquarium = 1
aquarium = 2
⇒ error: val cannot be reassigned

您可以為 fish 指派值,然後指派新值,因為這是以 var 定義。嘗試為 aquarium 指派新值會產生錯誤,因為該值是以 val 定義。

當編譯器可從內容判斷類型時,系統會推斷您儲存在變數中的類型。您隨時可以使用半形冒號標記法,明確指定變數的型別。

  1. 定義一些變數,並明確指定型別。
var fish: Int = 12
var lakes: Double = 2.5

一旦您或編譯器指派了型別,就無法變更型別,否則會發生錯誤。

步驟 4:瞭解字串

Kotlin 中的字串與其他程式設計語言中的字串非常相似,使用 " 表示字串,使用 ' 表示單一字元,並可使用 + 運算子串連字串。您可以將字串範本與值合併來建立字串範本;$variable 名稱會替換為代表該值的文字。這就是所謂的「變數插補」

  1. 建立字串範本。
val numberOfFish = 5
val numberOfPlants = 12
"I have $numberOfFish fish" + " and $numberOfPlants plants"
⇒ res20: kotlin.String = I have 5 fish and 12 plants
  1. 建立含有運算式的字串範本。如同其他語言,值可以是運算式的結果。使用大括號 {} 定義運算式。
"I have ${numberOfFish + numberOfPlants} fish and plants"
⇒ res21: kotlin.String = I have 17 fish and plants

在這項工作中,您將瞭解 Kotlin 程式設計語言中的布林值和檢查條件。與其他語言一樣,Kotlin 也有布林值和布林運算子,例如小於、等於、大於等等 (<==>!=<=>=)。

  1. 編寫 if/else 陳述式。
val numberOfFish = 50
val numberOfPlants = 23
if (numberOfFish > numberOfPlants) {
    println("Good ratio!") 
} else {
    println("Unhealthy ratio")
}
⇒ Good ratio!
  1. 請嘗試在 if 陳述式中使用範圍。在 Kotlin 中,您測試的條件也可以使用範圍。
val fish = 50
if (fish in 1..100) {
    println(fish)
}
⇒ 50
  1. 撰寫包含多個案例的 if。如需更複雜的條件,請使用邏輯 AND && 和邏輯 OR ||。與其他語言一樣,您可以使用 else if 建立多個案例。
if (numberOfFish == 0) {
    println("Empty tank")
} else if (numberOfFish < 40) {
    println("Got fish!")
} else {
    println("That's a lot of fish!")
}
⇒ That's a lot of fish!
  1. 試試 when 陳述式。在 Kotlin 中,您可以使用 when 陳述式,以更簡潔的方式編寫一系列 if/else if/else 陳述式,這類似於其他語言中的 switch 陳述式。when 陳述式中的條件也可以使用範圍。
when (numberOfFish) {
    0  -> println("Empty tank")
    in 1..39 -> println("Got fish!")
    else -> println("That's a lot of fish!")
}
⇒ That's a lot of fish!

在這項工作中,您將瞭解可為空值與不可為空值的變數。無關的程式設計錯誤是無數錯誤的根源。Kotlin 導入不可為空值的變數,以減少錯誤。

步驟 1:瞭解可空值性

根據預設,變數不能為 null

  1. 宣告 Int 並將 null 指派給該項目。
var rocks: Int = null
⇒ error: null can not be a value of a non-null type Int
  1. 在型別後方使用問號運算子 ?,表示變數可為空值。宣告 Int? 並將 null 指派給該項目。
var marbles: Int? = null

如果資料類型較為複雜 (例如清單):

  • 您可以允許清單的元素為空值。
  • 您可以允許清單為空值,但如果清單不是空值,則其元素不得為空值。
  • 您可以允許清單或元素為空值。

清單和其他複雜資料類型會在後續工作中介紹。

步驟 2:瞭解「?」和 ?: 運算子

您可以使用 ? 運算子測試 null,省去編寫許多 if/else 陳述式的麻煩。

  1. 以較長的方式編寫程式碼,檢查 fishFoodTreats 變數是否不是 null。然後遞減該變數。
var fishFoodTreats = 6
if (fishFoodTreats != null) {
    fishFoodTreats = fishFoodTreats.dec()
}
  1. 現在來看看如何使用 ? 運算子,以 Kotlin 方式編寫。
var fishFoodTreats = 6
fishFoodTreats = fishFoodTreats?.dec()
  1. 您也可以使用 ?: 運算子串連空值測試。請參閱以下範例:
fishFoodTreats = fishFoodTreats?.dec() ?: 0

這是簡寫,代表「如果 fishFoodTreats 不是 null,則遞減並使用該值;否則使用 ?: 後的值,也就是 0」。如果 fishFoodTreatsnull,系統會停止評估,且不會呼叫 dec() 方法。

空值指標的相關重點

如果您真的很喜歡 NullPointerExceptions,Kotlin 可讓您保留這些項目。非空值斷言運算子 !! (雙驚嘆號) 會將任何值轉換為非空值型別,如果值為 null,則會擲回例外狀況。

val len = s!!.length   // throws NullPointerException if s is null

在這項工作中,您將瞭解陣列和清單,以及在 Kotlin 程式設計語言中建立迴圈的不同方式。

步驟 1:製作清單

清單是 Kotlin 中的基本型別,與其他語言中的清單類似。

  1. 使用 listOf 宣告清單並列印出來。這份清單無法變更。
val school = listOf("mackerel", "trout", "halibut")
println(school)
⇒ [mackerel, trout, halibut]
  1. 使用 mutableListOf 宣告可變更的清單。移除項目。
val myList = mutableListOf("tuna", "salmon", "shark")
myList.remove("shark")
⇒ res36: kotlin.Boolean = true

如果 remove() 方法成功移除傳遞的項目,就會傳回 true

步驟 2:建立陣列

與其他語言一樣,Kotlin 也有陣列。與 Kotlin 中的清單不同 (有可變動和不可變動版本),Array 沒有可變動版本。建立陣列後,大小就會固定。您無法新增或移除元素,但可以複製到新陣列。

使用 valvar 的規則與陣列和清單相同。

  1. 使用 arrayOf 宣告字串陣列。使用 java.util.Arrays.toString() 陣列公用程式列印出來。
val school = arrayOf("shark", "salmon", "minnow")
println(java.util.Arrays.toString(school))
⇒ [shark, salmon, minnow]
  1. arrayOf 宣告的陣列不會與元素相關聯,因此您可以混合類型,這很有幫助。宣告具有不同型別的陣列。
val mix = arrayOf("fish", 2)
  1. 您也可以為所有元素宣告單一類型的陣列。使用 intArrayOf() 宣告整數陣列。其他型別的陣列也有對應的建構工具或例項函式。
val numbers = intArrayOf(1,2,3)
  1. 使用 + 運算子合併兩個陣列。
val numbers = intArrayOf(1,2,3)
val numbers3 = intArrayOf(4,5,6)
val foo2 = numbers3 + numbers
println(foo2[5])
=> 3
  1. 嘗試不同的巢狀陣列和清單組合。與其他語言一樣,您可以巢狀方式使用陣列和清單。也就是說,當您將陣列放入陣列中時,會得到陣列的陣列,而不是兩個陣列內容的扁平化陣列。陣列的元素也可以是清單,清單的元素也可以是陣列。
val numbers = intArrayOf(1, 2, 3)
val oceans = listOf("Atlantic", "Pacific")
val oddList = listOf(numbers, oceans, "salmon")
println(oddList)
⇒ [[I@89178b4, [Atlantic, Pacific], salmon]

第一個元素 numbersArray。如果您未使用陣列公用程式列印陣列,Kotlin 會列印位址,而不是陣列內容。

  1. Kotlin 的一項優點是,您可以使用程式碼初始化陣列,不必將陣列初始化為 0。請試試這個例句:
val array = Array (5) { it * 2 }
println(java.util.Arrays.toString(array))
⇒ [0, 2, 4, 6, 8]

初始化程式碼位於大括號之間,即 {}。在程式碼中,it 是指陣列索引,從 0 開始。

步驟 3:製作循環

現在您已擁有清單和陣列,可以按照預期方式逐一處理元素。

  1. 建立陣列。使用 for 迴圈疊代陣列並列印元素。
val school = arrayOf("shark", "salmon", "minnow")
for (element in school) {
    print(element + " ")
}
⇒ shark salmon minnow
  1. 在 Kotlin 中,您可以同時對元素和索引進行迴圈。請試試這個例句:
for ((index, element) in school.withIndex()) {
    println("Item at $index is $element\n")
}
⇒ Item at 0 is shark
Item at 1 is salmon
Item at 2 is minnow
  1. 請嘗試不同的步幅和範圍。您可以指定數字或字元的範圍 (依字母順序)。與其他語言一樣,您不必以 1 為步進值。你可以使用 downTo 倒轉。
for (i in 1..5) print(i)
⇒ 12345

for (i in 5 downTo 1) print(i)
⇒ 54321

for (i in 3..6 step 2) print(i)
⇒ 35

for (i in 'd'..'g') print (i)
⇒ defg
  1. 試用幾個迴圈。與其他語言一樣,Kotlin 也有 while 迴圈、do...while 迴圈,以及 ++-- 運算子。Kotlin 也有 repeat 迴圈。
var bubbles = 0
while (bubbles < 50) {
    bubbles++
}
println("$bubbles bubbles in the water\n")

do {
    bubbles--
} while (bubbles > 50)
println("$bubbles bubbles in the water\n")

repeat(2) {
     println("A fish is swimming")
}
⇒ 50 bubbles in the water
49 bubbles in the water
A fish is swimmingA fish is swimming

在運算子、清單和迴圈等基本概念方面,Kotlin 與其他語言非常相似,但仍有一些重要差異。

Kotlin 的下列功能可能與其他語言不同:

  • Kotlin 型別無法隱含轉換,請使用轉型。
  • 使用 val 宣告的變數只能指派一次。
  • 根據預設,Kotlin 變數不可為空值。使用 ? 將變數設為可為空值。
  • 使用 Kotlin 時,您可以在 for 迴圈中同時遍歷陣列的索引和元素。

下列 Kotlin 程式設計建構函式與其他語言中的建構函式類似:

  • 陣列和清單可以包含單一類型或混合類型。
  • 陣列和清單可以巢狀結構呈現。
  • 您可以使用 forwhiledo/whilerepeat 建立迴圈。
  • when 陳述式是 Kotlin 版本的 switch 陳述式,但 when 更具彈性。

Kotlin 說明文件

如要進一步瞭解本課程的任何主題,或遇到問題,請前往 https://kotlinlang.org

Kotlin 教學課程

https://try.kotlinlang.org 網站提供豐富的教學課程 (稱為 Kotlin Koans)、網頁式解譯器,以及附有範例的完整參考文件。

Udacity 課程

如要查看這個主題的 Udacity 課程,請參閱「程式設計人員的 Kotlin 新手上路課程」。

IntelliJ IDEA

IntelliJ IDEA 的說明文件位於 JetBrains 網站。

本節列出的作業可由課程講師指派給學習本程式碼研究室的學員。講師可自由採取以下行動:

  • 視需要指派作業。
  • 告知學員如何繳交作業。
  • 為作業評分。

講師可以視需求使用全部或部分建議內容,也可以自由指派任何其他合適的作業。

如果您是自行學習本程式碼研究室,不妨利用這些作業驗收學習成果。

回答問題

第 1 題

下列哪一項會宣告不可變更的字串清單?

val school = arrayOf("shark", "salmon", "minnow")

var school = arrayOf("shark", "salmon", "minnow")

val school = listOf("shark", "salmon", "minnow")

val school = mutableListOf("shark", "salmon", "minnow")

第 2 題

下列程式碼的輸出內容為何?
for (i in 3..8 step 2) print(i)

▢ 345678

▢ 468

▢ 38

▢ 357

第 3 題

這個程式碼中的問號用途為何?
var rocks: Int? = 3

▢ 變數 rocks 的類型不固定。

▢ 變數 rocks 可以設為空值。

▢ 變數 rocks 不得設為空值。

▢ 變數 rocks 不應立即初始化。

繼續下一個課程:3. 函式

如要查看課程總覽,包括其他程式碼研究室的連結,請參閱「程式設計人員的 Kotlin 新手上路課程:歡迎參加本課程。