---
# System prepended metadata

title: kotlin筆記–Data class
tags: [data, kotlin, data class, DataClass]

---

# kotlin筆記--Data class
在 <code>Kotlin</code> 中，<code>data class</code> 是「專門用來裝資料的類別」，主要目的不是處理商業邏輯，而是保存、傳遞、比較資料，提供了定義資料的結構格式。
可以把 <code>Data Class</code> 想成一張「資料表單」，表單上有固定欄位（屬性）。

<font size=4>主要用於:</font>
- 定義資料的格式
- 判斷兩個物件是否相等
- 把物件轉成字串（方便除錯）
- 複製物件並修改其中一個欄位

<font size=4><b>&lt; <u>範例</u> &gt;</b></font>
```kotlin=
// 宣告一個 Data Class，用來表示使用者資料
data class User(
    val id: Int,        // 使用者編號
    val name: String,   // 使用者名稱
    val email: String   // 使用者信箱
)
```

---
Data Class 的自動生成功能
---
<code>Kotlin</code> 的 <code>Data Class</code> 會依照主建構子中的屬性自動產生部分的重要成員函式

- <code>equals()</code>：用於比較兩個對象是否相等。
- <code>hashCode()</code>：用於生成對象的哈希碼。
- <code>toString()</code>：用於生成對象的字符串表示形式。
- <code>componentN()</code>：用於通過位置訪問對象的屬性（僅適用於具有多個屬性的類）。
- <code id="copy">copy()</code>：快速複製並修改部分屬性。


```kotlin=
data class User(
    val id: Int,        // 使用者編號
    val name: String,   // 使用者名稱
    val email: String   // 使用者信箱
)

val user1 = User(1, "PU", "pu@example.com")
val user2 = User(1, "PU", "pu@example.com")

// 1. 內容比較（Data Class 會自動比較屬性內容）
println(user1 == user2)  
// true

// 2. 印出內容
println(user1)  
// User(id=1, name=PU, email=pu@example.com)

// 3. 複製並修改
val user3 = user1.copy(email = "newpu@example.com")  
// 複製一份 user1，並且修改 user1 的 email

// 4. 解構
val (id, name, email) = user1
// id = 1
// name = "PU"
// email = "pu@example.com"
```

---
Data Class 的限制
---
<code>Kotlin</code> 將 <code>data class</code> 限定為「資料描述工具」，透過限制繼承與自動生成函式，換取一致性、可讀性與安全性。

- 主要建構子至少需要一個參數，且所有參數必須標記為 <code>val</code> 或 <code>var</code>
- 預設是 <code>final</code>，不能標記為 <code>abstract</code>、<code>open</code>、<code>sealed</code> 或 <code>inner</code>
- data class 不能被繼承但可以繼承別人
  - 不推薦繼承別人，需要繼承的話最好是使用普通的 Class
  - 繼承的限制: 父類必須是 <code>open</code>，且必須在主建構子中呼叫父類建構子
  - 父類屬性不會被納入自動生成函式
- 不能被繼承，但可以實作介面
  ```kotlin=
  interface Identifiable {
      val id: String
  }

  data class User(
      override val id: String,
      val name: String
  ) : Identifiable
  ```

---
<code>val</code>/<code>var</code> 的層次關係與 <code>DataClass</code>
---

在實務上，當我們在 [ViewModel](https://hackmd.io/@9YAtszqXS2OLNZOrLY_-Jg/android_ViewModel) 中使用 [LiveData](https://hackmd.io/@9YAtszqXS2OLNZOrLY_-Jg/android_liveData)、[mutableStateOf()](https://hackmd.io/MR6tFrBcSbWvy-8eQA6deg#mutableStateOf-%E8%88%87-ViewModel-%E5%8D%94%E4%BD%9C) 時經常會碰到一個問題 -- 明明變數宣告成 <code>val</code>，為什麼內容還是可以被修改？
>這是因為 <code>val</code> 限制的是「變數本身不能重新指向其他物件」，而不是限制物件內部的屬性不能變更。


<font size=4><b>&lt; <u>以下以一個簡單的 <code>DataClass</code> 和 <code>ViewModel</code> 舉例</u> &gt;</b></font>
<details><summary>範例</summary>
<div>

```kotlin=
// DataClass
data class NameCardState(
    val name: String,
)
```
```kotlin=
// ViewModel

class MyViewModel(initialState: NameCardState) : ViewModel() {
    private val _state = mutableStateOf(initialState)
    val state: State<NameCardState> = _state

    // data class 的欄位是 val，不能直接修改
    // 必須透過 copy() 產生新物件來替換 _state.value
    fun onNameChange(newName: String) {
        _state.value = _state.value.copy(name = newName)
    }
}
```
<font size=3><b>&lt; <u>以保險箱和護貝名片舉例</u> &gt;</b></font>
><code>_state</code>、<code>_state.value</code>、<code>_state.value.name</code>，就像是保險箱、護貝名片、護貝名片上的字
* 第一次執行 <code>val _state = mutableStateOf("名片")</code> , 就是把一張護貝的名片放進去, 然後 <code>_state</code> 會被視為保險箱, 而這張名片就會被視為 <code>_state.value</code>
* 第二次如果打算執行 <code>val _state = mutableStateOf("名片")</code>, 會被視為打算換掉整個保險箱(<code>val _state</code>)而失敗
* 雖然不能替換保險箱(<code>_state</code>)，但是 ==[LiveData](https://hackmd.io/@9YAtszqXS2OLNZOrLY_-Jg/android_liveData)、[mutableStateOf()](https://hackmd.io/MR6tFrBcSbWvy-8eQA6deg#mutableStateOf-%E8%88%87-ViewModel-%E5%8D%94%E4%BD%9C) 有提供我們一個 <code>.value 屬性</code> 使我們可以替換保險箱內部的護貝名片(<code>_state.value</code>)==
* 但是由於這張名片(<code>_state.value</code>)上的名子(<code>_state.value.name</code>)是 <code>val</code>(有護貝), 所以名片上的字不能改
    
    

</div>
</details>
