---
# System prepended metadata

title: Kotlin / Ch01 - Kotlin Fundamental

---

# Kotlin / Ch01 - Kotlin Fundamental

## 第一章：Kotlin 簡介

### 1.1 什麼是 Kotlin

Kotlin 是一種現代化的程式語言，由 JetBrains 公司開發，名稱來源於俄羅斯的柯特林島 (Kotlin Island)。作為 JVM (Java Virtual Machine) 上的語言，Kotlin 與 Java 完全互通，同時提供了更簡潔和安全的程式設計體驗。

### 1.2 Kotlin 的發展背景

在程式語言的生態系統中，Java 由 Sun Microsystems (後被 Oracle 收購) 開發，主要使用 Eclipse IDE 進行開發。而 Kotlin 則是由 JetBrains 公司創造，搭配 IntelliJ IDEA 作為主要的開發環境。兩種語言都運行在 JVM 上，最終編譯成相同的 Binary Code，這意味著它們可以無縫互通。

```mermaid
graph TD
    A[Kotlin] --> D[JVM]
    B[Java] --> D[JVM]
    D --> E[Binary Code]
```

### 1.3 為什麼選擇 Kotlin

#### 1.3.1 官方支援地位

Google 於 2017 年宣布 Kotlin 為 Android 應用程式開發的官方語言，這標誌著 Kotlin 在行動應用開發領域的重要地位。開發者可以使用 Kotlin 來建構完整的 Android 應用程式，享受與 Java 相同的功能但具備更佳的語言特性。

#### 1.3.2 後端開發的優勢

在後端開發方面，Spring Framework 對 Kotlin 提供了一流的支援。Spring 框架讓開發者能夠撰寫 Kotlin 應用程式，幾乎就像 Spring Framework 本身就是原生的 Kotlin 框架一樣。官方文件中的大部分程式碼範例都同時提供 Kotlin 和 Java 版本，顯示了對 Kotlin 的重視程度。

#### 1.3.3 與其他框架的比較

相比於傳統的 Spring 框架，Kotlin 也支援其他輕量級框架如 Ktor。Spring 是一個成熟的框架系列，擁有完整的生態系統，被全世界數百萬開發者使用。而 Ktor 則是為重視架構設計自由度的開發者提供的輕量級選擇，讓他們能夠建立 HTTP API、WebSocket 聊天應用，以及互動式網站。

## 第二章：基本語法與資料型別

### 2.1 資料型別系統

Kotlin 的型別系統與 Java 有顯著差異。在 Kotlin 中，所有的型別都是物件，沒有 Java 中的基礎型別 (primitive types) 如 `int`、`float` 等。

#### 2.1.1 數值型別

Kotlin 提供以下數值型別，按照大小排序：
- **Long**：64 位元整數
- **Int**：32 位元整數  
- **Short**：16 位元整數
- **Byte**：8 位元整數
- **Double**：64 位元浮點數
- **Float**：32 位元浮點數

#### 2.1.2 其他基本型別

- **Char**：字元型別
- **Boolean**：布林值 (true/false)

### 2.2 變數宣告

Kotlin 使用關鍵字來宣告變數，並支援型別推斷，這意味著編譯器能夠自動判斷變數的型別。語法結構不需要使用分號 (`;`) 結尾。

#### 2.2.1 變數宣告關鍵字

| 關鍵字 | 特性 | 等同於 Java | 範例 |
|--------|------|-------------|------|
| `var` | 可變變數 | 一般變數宣告 | `var a = 100` |
| `val` | 不可變變數 | `final` 修飾符 | `val b = 20` |
| `const val` | 編譯時期常數 | `static final` | 只能在全域範圍定義 |

#### 2.2.2 實際範例

```kotlin=
fun main() {
    var a = 100
    a = 150
    // a = "Boy" // 錯誤：var 推導後型別不可變
    println("a = $a")
    
    val b = 20
    // b = 25 // 錯誤：val 描述後不可再賦值
    println("b = $b")
    
    var d: Short = 1 // 明確指定型別
    d = 11
    println("d = $d")
    
    // String Interpolation (字串插值)
    var e = 174
    var c: String = "$e ${e + 33} Interpolation"
    println(c)
}
```

### 2.3 函數基礎

#### 2.3.1 函數語法結構

在 Kotlin 中，函數使用 `fun` 關鍵字定義。基本語法如下：

```kotlin=
fun functionName(parameter1: Type, parameter2: Type): ReturnType {
    // 函數主體
    return result
}
```

重要特點：
- `fun` 用於宣告函數
- `main()` 函數是程式的進入點
- 函數主體使用大括弧 `{}` 包圍
- 參數寫在圓括弧內，每個參數必須指定型別
- 回傳型別寫在函數括弧後，用冒號 `:` 分隔
- 如果函數不回傳有用的值，回傳型別和 `return` 關鍵字可以省略

#### 2.3.2 字串插值 (String Interpolation)

Kotlin 提供強大的字串插值功能，讓開發者能夠輕鬆地在字串中嵌入變數值，~~相當有 JavaScript 的既視感~~：

```kotlin=
val name = "Kotlin"
val version = 1.8
val message = "Hello, $name! Version: $version"
val calculation = "計算結果：${10 + 20}"
```

## 第三章：條件控制與流程

### 3.1 if/else 條件判斷

Kotlin 中的 `if` 語句可以作為表達式使用，這意味著它可以回傳值而不需要明確使用 `return` 關鍵字。

```kotlin=
fun max(a: Int, b: Int): Int {
    // if 作為表達式，直接回傳最大值
    val result = if (a > b) a else b
    return result
}

fun main() {
    val x = 10
    val y = 20
    println("The max value is: ${max(x, y)}")
}
```

### 3.2 when 語句

`when` 是 Kotlin 用來取代 Java 中 `switch-case` 語句的現代化替代方案，提供更強大和靈活的功能。

```kotlin=
fun useWhenSimple(id: Int) = when(id) {
    1 -> "A"
    2 -> {  // 多行時使用大括弧
        "B"
    }
    3 -> "C"
    else -> "Non"  // 類似 default
}

// 型別檢查 (類似 Java 的 instanceof)
fun useIs(id: Number): Unit = when(id) {
    is Int -> println("參數是 Int 型別")
    is Double -> println("參數是 Double 型別")
    is Float -> println("參數是 Float 型別")
    else -> println("無法判斷型別")
}
```

範例中用到的 `->` 符號稱為 Lambda 表達式，先不用太在意有個印象就好，等到第四章我們再來詳細聊它。

### 3.3 相等性比較

Kotlin 提供兩種相等性比較方式，與 Java 的概念不同：

| 功能 | Java | Kotlin |
|------|------|--------|
| 內容相等 | `.equals()` | `==` |
| 參考相等 | `==` | `===` |

```kotlin=
var a: String = "Hello"
var b: String = String(StringBuffer("Hello"))

println("a.equals(b): ${a.equals(b)}")  // true
println("a == b: ${a == b}")             // true
println("a === b: ${a === b}")           // false
```

這種設計讓程式碼更加直觀，因為大多數情況下開發者關心的是內容是否相等，而非物件參考是否相同。另外，關於 StringBuffer 的介紹可以參考：[Kotlin / Ch02 - 並行程式設計 第三章](https://hackmd.io/Pik-HRodS0eRQmUOYGcq5g?view#%E7%AC%AC%E4%B8%89%E7%AB%A0%EF%BC%9AMutable-%EF%BC%86-Immutable)

## 第四章：Lambda 表達式與高階函數

### 4.1 Lambda 表達式基礎

Lambda 表達式，又稱匿名函數 (Anonymous Function)，是 Kotlin 函數式程式設計的重要概念。Lambda 表達式讓開發者能夠將函數當作值來傳遞和操作。

#### 4.1.1 Lambda 語法結構

Lambda 表達式的基本語法：
```kotlin
val lambdaName: Type = { parameter: Type -> body }
```

Lambda 表達式包含兩個主要部分：

**型別部分 (Type Part)**
- `(Int, Int) -> Unit`：代表兩個 Int 參數，無回傳值
- `(Int, Int) -> Int`：代表兩個 Int 參數，回傳 Int 型別

**物件部分 (Object Part)**
- 參數清單在 `->` 前面
- 函數主體在 `->` 後面
- 最後一行會作為回傳值

#### 4.1.2 實際範例

```kotlin=
val square: (Int) -> Int = { x -> x * x }
println(square(5))  // 輸出 25

val printInfo: (String) -> Unit = { name ->
    println("名字: $name")
    println("字數: ${name.length}")
}
printInfo("Kotlin")
```

### 4.2 高階函數 (Higher-Order Function)

高階函數是指接收其他函數作為參數，或者回傳函數的函數。這是函數式程式設計的核心概念。

#### 4.2.1 函數作為參數

```kotlin=
fun passMeFunction(abc: () -> Unit) {
    abc()  // 或者使用 abc.invoke()
}

fun main() {
    passMeFunction { println("Hello !") }
}
```

#### 4.2.2 函數作為回傳值

```kotlin=
fun returnMeAddFunction(): ((Int, Int) -> Int) {
    return { a: Int, b: Int -> a + b }
}

fun main() {
    val add = returnMeAddFunction()
    val result = add(2, 2)
    println(result)  // 輸出 4
}
```

### 4.3 Lambda 表達式的語法簡化

#### 4.3.1 使用 it 參數

當 Lambda 表達式只有一個參數時，可以使用 `it` 來代替明確的參數名稱：

```kotlin
val selectedPerson = people.maxBy({ person -> person.age })
val selectedPerson = people.maxBy({ it.age })  // 簡化版本
```

#### 4.3.2 移除圓括弧

當 Lambda 表達式是函數的最後一個參數時，可以將其移到圓括弧外，當然這有時候會取決於公司的 Linter 規範：

```kotlin
passMeFunction({ println("Hello !") })
passMeFunction { println("Hello !") }  // 簡化版本
```

### 4.4 Lambda 表達式常見用法

| 類型/功能 | 語法 | 範例 | 輸出 |
|----------|------|------|------|
| 基本語法 | `{ 參數 -> 主體 }` | `val sum = { a: Int, b: Int -> a + b }` | 8 |
| 單參數省略 | `{ it * it }` | `listOf(1,2,3).map { it * it }` | [1, 4, 9] |
| 無參數 | `{ -> 值 }` 或 `{ }` | `val hello: () -> String = { "Hi!" }` | Hi! |
| 忽略參數 | `{ x, _ -> x }` | `val f: (Int, Int) -> Int = { x, _ -> x }` | 5 |
| 傳入高階函數 | 函數(參數) `{ Lambda }` | `listOf(1,2,3).filter { it > 1 }` | [2, 3] |
| fold 聚合 | `{ acc, elem -> 處理 }` | `listOf("a","b","c").fold("") { acc, s -> acc + s }` | abc |

### 4.5 Lambda 與普通函數的比較

| 特性 | 普通函數 | Lambda |
|------|----------|--------|
| 是否有名字 | ✓ | ✗ 匿名 |
| 是否能當值傳遞 | 需要函數型別包裝 | ✓ 可直接當值 |
| 常見用途 | 一般邏輯 | 集合處理、回呼函式、DSL |

個人的小小心得，非常推薦在剛開始學習的時候，用力把每個表達式都看到真的懂，不要大概大概就好。久了之後看這些表達式的速度才會越來越快，熟了之後甚至我體感會覺得是在看整個程式的「形狀」，也就是腦袋肌肉記憶的一部份了。

## 第五章：集合物件 (Collections)

### 5.1 集合類型概述

Kotlin 提供三種主要的集合類型，每種都有其特定的用途和特性：

| 集合類型 | 描述 | 特性 |
|----------|------|------|
| Lists | 有序的項目集合 | 允許重複元素，保持插入順序 |
| Sets | 唯一無序的項目集合 | 不允許重複元素，無特定順序 |
| Maps | 鍵值對的集合 | 鍵必須唯一，可以有重複的值 |

### 5.2 可變與不可變集合

Kotlin 中的每種集合都有可變 (Mutable) 和不可變 (Immutable) 兩個版本。關於為什麼 Kotlin 要這樣設計，這牽涉到比較進階的內容（multi-threading & GC），所以在這邊就不多做贅述：

#### 5.2.1 List 範例

```kotlin=
// 不可變 List
val immutableList = listOf("Hung", "Jui", "Chung", "Hung")
println(immutableList.size)        // 4
println(immutableList.indexOf("Jui"))  // 1，找不到回傳 -1
println(immutableList[1])          // "Jui"

// 可變 List
val mutableList = mutableListOf("Hung", "Jui", "Chung")
mutableList.add("Android")  // immutableList.add() 會出錯
println(mutableList)
```

#### 5.2.2 Set 範例

```kotlin
// Set 自動去除重複元素
val set = setOf("Hung", "Jui", "Chung", "Hung")
println(set)  // [Hung, Jui, Chung] - "Hung" 只出現一次
```

#### 5.2.3 Map 範例

```kotlin=
val map = mutableMapOf(
    "Hung" to 0,
    "Jui" to 1,
    "Chung" to 1,
    "Mars" to 2,
    "Android" to 79,
    "Java" to 82,
    "Kotlin" to 27
)

println(map.size)       // 7
map["C++"] = 5          // 新增元素
map["Jui"] = 100        // 更新現有元素
println(map.size)       // 8
println(map["Jui"])     // 100
```

### 5.3 集合操作與 Lambda

集合與 Lambda 表達式結合使用時，能夠實現強大的資料處理功能：

```kotlin=
val names = listOf("Hung", "Jui", "Chung")

// 使用 withIndex() 同時取得索引和值
for ((index, value) in names.withIndex()) {
    println("$index: $value")
}

// 使用 Lambda 進行集合處理
names.filter { it.length > 3 }  // 篩選長度大於3的名字
names.map { it.uppercase() }    // 轉換為大寫
```

## 第六章：迴圈與區間操作

Kotlin 提供豐富的區間操作符來控制迭代行為：

| 操作符 | 說明 | 範例 |
|--------|------|------|
| `..` | 包含終點的區間 | `0..5` 包含 0,1,2,3,4,5 |
| `until` | 不包含終點的區間 | `0 until 5` 包含 0,1,2,3,4 |
| `step` | 指定步長 | `0 until 10 step 2` 輸出 0,2,4,6,8 |
| `downTo` | 降序區間 | `10 downTo 0 step 2` 輸出 10,8,6,4,2,0 |
| `in` | 判斷是否在區間內 | `if (x in 1..10)` |

```kotlin=
// 各種區間操作範例
for (i in 0..5) print("$i ")              // 0 1 2 3 4 5
for (x in 0 until 5) println(x)           // 0 1 2 3 4
for (x in 0 until 10 step 2) print("$x ") // 0 2 4 6 8
for (x in 10 downTo 0 step 2) println("$x ") // 10 8 6 4 2 0
```

## 第七章：函數進階概念

### 7.1 函數語法結構

完整的函數定義語法：

```kotlin
fun [函數名稱]([參數1名稱]: [參數1型別], [...其他參數]): [回傳型別] {
    [函數內容]
}
```

### 7.2 函數簡化語法

#### 7.2.1 單行函數簡化

當函數只有一行時，可以使用等號 (`=`) 來簡化：

```kotlin=
// 傳統寫法
fun happyBirthday1(name: String, age: Int): String {
    return "Happy ${age}th birthday, $name!"
}

// 簡化寫法
fun happyBirthday1(name: String, age: Int) = "Happy ${age}th birthday, $name!"
```

#### 7.2.2 Unit 回傳型別

當函數不需要回傳有意義的值時，可以省略 `Unit` 型別：

```kotlin=
fun happyBirthday2(name: String, age: Int): Unit = 
    println("Happy ${age}th birthday, $name!")

// 可以省略 Unit
fun happyBirthday2(name: String, age: Int) = 
    println("Happy ${age}th birthday, $name!")
```

### 7.3 高階函數應用

```kotlin=
fun operateTwoNumbers(a: Int, b: Int, f: (Int, Int) -> Int) = f(a, b)

fun main() {
    val multiple = operateTwoNumbers(5, 15) { a, b -> a * b }
    val add = operateTwoNumbers(5, 15) { a, b -> a + b }
    println(multiple)  // 75
    println(add)       // 20
}
```

這種設計模式讓函數變得極其靈活，能夠根據傳入的 Lambda 表達式執行不同的操作邏輯。

## 第八章：Null Safety (空值安全)

### 8.1 Java 與 Kotlin 的差異

傳統的 Java 程式設計中，`NullPointerException` 是最常見的執行時期錯誤之一。Java 在編譯階段並不會檢查空值問題，只有在程式執行時才會發現問題：

```java=
// Java 範例 - 編譯通過但執行時出錯
String name = null;
System.out.println(name.length()); // NullPointerException
```

### 8.2 Kotlin 的解決方案

Kotlin 透過型別系統在編譯時期就防止了空值相關的錯誤。在 Kotlin 中，只有在型別後加上問號 (`?`) 的變數才能接受空值：

```kotlin=
val message: String? = null  // 可以為 null
val name: String = "Kotlin"  // 不能為 null

val name: String? = null
// println(name.length()) // 編譯錯誤：無法直接呼叫可空型別的方法
```

### 8.3 處理可空型別

Kotlin 提供多種安全處理可空型別的方式：

#### 8.3.1 安全呼叫運算子 (?.)

```kotlin
val length = name?.length  
// 如果 name 不為 null，回傳長度；否則回傳 null
```

#### 8.3.2 Elvis 運算子 (?:)

```kotlin
val length = name?.length ?: 0  
// 如果結果為 null，使用預設值 0
```

#### 8.3.3 強制解包運算子 (!!)

```kotlin
val length = name!!.length  
// 強制視為非空值，但如果為 null 會拋出異常
```

這種設計讓開發者在編寫程式時就必須考慮空值的情況，大大減少了執行時期因空值而產生的錯誤，提升了程式的穩定性和可靠性。