iT邦幫忙

2022 iThome 鐵人賽

DAY 5
0
Mobile Development

【Kotlin Notes And JetPack】Build an App系列 第 5

Day 5.【Classes and Objects】Sealed Classes

  • 分享至 

  • xImage
  •  

接下來幾天會開始將筆記加入實作的 App ,前面第一階段的什麼?還是以筆記的型態講解每篇的主題,第二階段的如何?會開始以 app 的結構開始實作,以下如有解釋不清或是描述錯誤的地方還請大家多多指教:

什麼?

sealed class 跟 interfaces 都是被設定為受限的繼承結構。sealed class 本身是抽象類別,本身不能直接實例化,而 constructor 必須為 protected 或是 private ,subclass 需要有命名,不可為 local 或是匿名物件,檔案位置也須和 sealed class 在同個 package 中 。

sealed class Character {
	// protected by default
    constructor() { /*...*/ } 
	// private is OK
    private constructor(description: String): this() { /*...*/ } 
	// Error: public and internal are not allowed
    public constructor(code: Int): this() {}  
}

kotlin 1.0 時 subclass 必須在 sealed class 內部
kotlin 1.1 subclass 不必在 sealed class 內部,但仍需要在同個文件中
kotlin 1.5 可定義在不同檔案中,但需要在同個 package 中

因為這樣的結構讓我們可以透過 sealed class 來呈現資料處理狀態,像是一個購物清單的頁面物件需要有讀取中、顯示、空的、錯誤等等狀態,可以針對購物清單設定一個 sealed class 和各個需要的狀態的 subclass,這些資料狀態就只限於購物清單。或者是今天要切分各個錯誤類別,因為讀取資料造成 IOError 的,可能分成檔案讀取、資料庫讀取等等。

sealed class ShopListState(val id: Int)

data class ShopLoadingState(val shopId: Int): ShopListState(shopId)
data class ShopData(val shopId: Int, val source: shopData): ShopListState(shopId)
data class ShopError(val shopId: Int, val error: String): ShopListState(shopId)

某種程度上和之後會提到的 enum class 很像,只是差異在 sealed class 的 subclass 可以持有多個擁有自己狀態的 instances ,而 enum 的存在被視為單ㄧ instance。

enum classes can't extend a sealed class (as well as any other class), but they can implement sealed interfaces.

| Sealed interfaces

上面有提到 sealed class 跟 sealed interfaces,那這兩者之間又有什麼不一樣呢?sealed interfaces 是在 Kotlin 1.5.0 才推出的,兩者都有 compile 過後就不能被第三方實作的特性,以下整理出幾個特點:

  • 不可被第三方任意實作的 interface
interface Human {
	fun walk()
	fun eat()
}

// 當想要開出不想被第三方實作的接口時,就可以用 sealed class 
sealed interface Human {
	fun walk()
	fun eat()
}
  • 可以使語言單繼承結構延伸更多使用場景

enum

enum class Action {
	WALK, RUN, JUMP
}
// 透過反譯可以得知 action 繼承了 Enum,因為單繼承的結構所以 enum 不行繼承其他 class,
// 但可以 implement 多個 interface

sealed interface Human
enum class Action : Human {
	WALK, RUN, JUMP
}

enum class Age : Human {
	KID, TEENAGER, ADULT, ELDERLY
}

object or class

// 因為 sealed class 為抽象類,每個物件都只能繼承一個抽象類別,當要延伸較複雜的結構關係時
sealed interface Error
sealed interface Runtime

sealed class IOError(): Error

class FileReadError(val file: File): IOError()
class DatabaseError(val source: DataSource): IOError()

object RuntimeError : Error, Runtime

如何?

app 有兩個部分會用到 sealed class ,就如上面提到的 sealed class 很適合用在元件的資料狀態,以及切分錯誤類別,我會在 app 有打 api 的頁面都建立一個 UI state sealed class 以及建立一個接 api response 的 sealed class 切分 loading, success, error 的狀態。如以下:

// 以下分別建立在 home, detail, search package 中
sealed class HomeVo
sealed class DetailVo
sealed class CitiesVo

https://ithelp.ithome.com.tw/upload/images/20220918/20151145Q720YOCcbD.png

// 之後接 API 時就可以知道資料回傳成功或失敗及 loading 的狀態
sealed class Resource<T>(
	val data: T? = null,
	val message: String? = null
) {
	class Loading<T> : Resource<T>()
	class Success<T>(data: T) : Resource<T>(data = data)
	class Error<T>(errorMessage: String) : Resource<T>(message = errorMessage)
}

UI state 的 subclass 在後續的篇章再繼續補上,目前先開出需要用到的 sealed class,基本上成功會有 display state, empty state,失敗和 loading 都會以 resource 送出去的狀態決定。

Reference

Reference
How to Handle API Responses (Success/Error) in Android?
Sealed Interface


上一篇
Day 4.【Functions】Lambdas
下一篇
Day 6.【Classes and Objects】Data Classes
系列文
【Kotlin Notes And JetPack】Build an App30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言