# 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>