sealed classを理解する
目次
sealed classとは
普通のclassだと無限に拡張される可能性がある型となりますが、sealed classは取りうる形をここで全部決め切る型のためSwiftでいうenumのようなものとなります。
sealed classの定義
sealed classは継承が利用されることを前提として作成されます。
sealed class内ではobjectとしてLoadStateを継承したものを定義します。
sealed class LoadState {
// シングルトンとしてIdleオブジェクトを定義(LoadStateを継承している)
object Idle : LoadState()
object Loading : LoadState()
data class Success(val data: Data) : LoadState()
data class Error(val error: Throwable) : LoadState()
}継承に括弧が必要な理由
上記のLoadStateになぜ括弧が必要かと言うとsealed class LoadStateと定義しても実際にはsealed class LoadState()として定義されます。Kotlinの言語仕様として親クラスにコンストラクタがある場合に必ず呼ばなければならないためです。
objectで定義する
Kotlinにおけるobjectはインスタンスは1つで状態を保持しないSwiftでいうenumそのものだからです。
// これと同じ
LoadState.Idledata classで定義する
値を保持する場合はdata classを利用します。
なぜ普通のclassではなくdata classを利用するのか、普通のクラスではれば同じ内容でも等価性で判断するため別物として扱われます。しかしdata classの場合は中身が同じなら同じ(value object)として扱われます。
data classはequals()、hashcode()、toString()、copy()メソッドを利用することができるため、 状態と非常に相性がいいという性質があります。
Composeでは「前回と今回の値が等しいか」で再コンポーズするかを判断することになるのでここがclassになっていると別物として扱われるので同じ状態でも変更時には再コンポーズされてしまいます。
こうすることで下記のSwiftのように合計4つの状態を表すことが可能となります。
enum LoadState {
case idle
case loading
case success(Data)
case error(Error)
}objectはdata classは相互に参照できる?
LoadState
/ \
Idle Success
技術的に可能ですが横方向の参照関係はないく、親を通じて同じ名前空間にいるだけです。
同じLoadStateとして扱うにはIdleもSuccessもLoadStateを継承する必要がありますのでseal classの中で隠蔽しているという形ですね。
使い方
var state: LoadState = .idle
state = .success(data)
state = .idle上記では値を保持する必要がない初期値では.idleとなっていて、値を保持する場合は.success(data)を利用しています。
Composeで利用する場合
@Composable
fun Screen(viewModel: SampleViewModel) {
val state = viewModel.uiState
when (state) {
UiState.Idle -> IdleView()
UiState.Loading -> LoadingView()
is UiState.Success -> ContentView(state.data)
is UiState.Error -> ErrorView(state.message)
}
}
class SampleViewModel : ViewModel() {
private val _uiState = MutableStateFlow<UiState>(UiState.Idle)
val uiState: StateFlow<UiState> = _uiState
fun load() {
_uiState.value = UiState.Loading
}
}これの何がいいのかというと、isによってスマートキャストされると自動的にその型に変換され、Successであればdataを利用することが可能になるという点です。
Composeとも相性がいいという事がわかりました!