配合分支:day06/start,Repo: 按我
知道資料流的觀念後,本章進入程式碼結構基礎介紹。前一則笨問題發現是數值塞錯地方造成的問題。完成「新增照片」的表格後 DiaryForm 會啟動一個 callback function,將標題及內文帶回呼叫點。
儲存時發生了什麼事情?

把 DiaryEntry 看起來展開一樣塞進資料,並加到 entries 列表之中。先來看看 DiaryEntry 是什麼東西。在 Android Studio 裡頭,用 Cmd + B (Windows/Linux: Ctrl + B) 前往這個東西的實作。

這東西是一個 data class!和 MainActivity 有什麼差異?它也有 class。 🤔 一般來說,class (中文翻譯是類別,中國翻成「類」) 像是一個規格書。注意到 class 裡頭有很多 function (函式) 可以做事,可以存變數紀錄狀態。
一般來說 class 可以透過建構子 (Constructor) 將它實作出來用,像是:
class Car {
// ... 省略
}
可以這樣把它召喚出來:Car()。 此數,能用一個變數將它接起來:
val car = Car()
透過建構子得到的東西,叫做 Instance (翻譯是:實例),反正你會得到一個 Instance,可以操弄它。
不過這裡也提醒 MainActivity 是來自 Android 的元件,他有自己的建構方式,所以在照片日記裡頭看不到他的建構子。(aka. 無法透過 MainActivity() 得到一個 Instance)
不過前面不是提到變數是 var abcde = <數值> 嗎?這裡出現了 val?
兩者的差異是 val 一開始賦予數值後,就無法更改了。但是 var 可以改!
val name = "RayLiu"
name = "RayYuan" // 🥲 不能改
// vs
var name = "RayLiu"
name = "RayYuan" // 成功!改成 "RayYuan"
雖然 var 很方便,不過出問題時要找也非常麻煩,要確認到底哪邊會把數值改掉。經過前兩次的教訓,在一個實務上在跑的系統裡,用 var 要追查問題絕對是更為複雜的!
val 因為只有賦予數值一次,之後只要去查資料流怎麼流動就行了。現代的程式開發也建議絕大多數的情境,用 val 就好了!
回到 class 的話題:畫面上有三篇日記。它們都有標題、內容、心情。這三篇日記是三種不同的東西,還是同一種東西的三個實例?
答案是後者。DiaryEntry 是「日記這種東西」的規格,實際建立出的每一篇日記才是物件。所以在 DiaryForm 回傳時,可以這樣子接住實例,並塞到列表裡頭。

啊,你可能覺得覺得很怪,為什麼還要塞到一個新的列表裡頭,再加入 entries 裡頭。
entries = listOf(newEntry) + entries
註:listOf(物件...) 會吐出物件的列表:List<物件> 。
是因為要把這個新加入的日記放在最前面(也就是最上面),如果沒有要這樣做,放最後面,那直接:
entries = entries + newEntry
就好。
那 data class 差別到底是什麼?data class 其實是 Kotlin 的語法糖 :-P ,提供一些好東西。比如說要複製一篇照片日記:
// 假設 要複製 oldEntry
val newEntry = DiaryEntry(
photoResId = oldEntry.protoResId,
title = oldEntry.title,
note = oldEntry.note,
mood = "亢奮", // <== 改了這個,其他一樣
createdAt = oldEntry.createdAt
)
data class 只需要:
val newEntry = oldEntry.copy(mood = "亢奮")
// 沒有要改亢奮,就直接 copy 就好
val newEntry = oldEntry.copy()
非常爽。當然不只這些,總之 data class 已經對傳遞資料做了各種好用的處理,總之需要傳遞資料時,記得想到他。
data class。class 。就是黏在 class 上的變數,例如:
class Car {
// 👇🏼 這些黏在 class 上的變數,即是 properties
private val name = "Toyota"
var price = 600000
private val isGas = true
}
或是建構的時候需要帶的:
class Car(
private val year: Int // 👈🏼 這也是
) {
private val name = "Toyota"
var price = 600000
private val isGas = true
}
data class 裡頭的這些也是:
data class DiaryEntry(
// 👇🏼 這些也是 properties
val title: String,
val note: String,
)
這些 class 被 initialize (中文好像是:實例化,也就是上面說的召喚出來,通常工程師會說 Init )後,可以透過變數存取它。例如改價格 (ㄏ)
val car = Car(year = 2022)
car.price = 777 // 👈🏼 可以這樣存取 properties,如果是 var 就能改
註:出現了第三個 Type: Int,一般來說就是拿來存數字的。
而存取 properties 前幾天我們已經示範過囉!就是像這樣 car.title 取資料。
Car() 這種寫法把 class 召喚出來;沒有這一步,寫好的規格還不能拿來用。val car = Car();畫面上三篇日記就是三個實例。val 就用 val。DiaryEntry 就是日記的規格。List<物件>。oldEntry.copy(mood = "亢奮"),不必把每個欄位重抄一遍。class;只有保存與傳遞資料才用 data class。car.price 這種寫法存取,var 的才能改。