Kotlin sealed classを使ってみた!
対象者
- Kotlinに興味がある人
- Androidアプリに興味がある人
sealed classとは?
シールされたクラスとインターフェースは、クラス階層の制御された継承を提供します。シールされたクラスの直接のサブクラスはコンパイル時にすべてわかります。シールドされたクラスが定義されているモジュールやパッケージの外では、 他のサブクラスが出現することはありません。同じ理屈がシールドされたインターフェースとその実装にも当てはまります。一度シールドされたインターフェースを持つモジュールがコンパイルされると、新しい実装を作成することはできません。
そのまま翻訳だとわかりずらいな...
sealed classは、そのクラスの継承を制限することができるクラスです。
sealed classを継承できるサブクラスは、sealed classが定義されているモジュール/ファイル内に限定されます。
つまり、sealed classの外部からサブクラスを継承することはできません。
このようにサブクラスが限定されることで、sealed classのインスタンスがどのサブクラスのインスタンスであるかを網羅的に扱うことができます。
そのため、when式を使ってパターンマッチングをする際に、すべてのパターンを網羅的に扱う必要があります。
これにより、不完全なパターンマッチングのエラーを防ぐことができます。
sealed classは、代数的データ型のようにデータの種類を列挙し、それぞれのデータに応じた処理を関数内で記述できるようになります。状態マシンやエラーハンドリングなど、ある種類のデータに対してケースバイケースで処理を分ける必要がある場合に、sealed classは非常に役立ちます。
今回は、Objectを切り替えるロジックを作ってみました。
package com.example.sealedapp
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.sealedapp.ui.theme.SealedAppTheme
// Boxの状態を表すsealed class
// Red, Green, Blueの3つの状態を持つ
sealed class BoxState {
data object Red : BoxState()
data object Green : BoxState()
data object Blue : BoxState()
}
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
SealedAppTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
ToggleBox()
}
}
}
}
}
// BoxStateを受け取り、それに応じて色を変えるBoxを描画する
@Composable
fun ToggleBox() {
// Boxの状態を保持するmutableState
// Genericsの型にはBoxStateを指定。初期値はBoxState.Red
// rememberを使って再構築時に状態を保持する
var boxState by remember { mutableStateOf<BoxState>(BoxState.Red) }
// Columnを使って縦方向に配置
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.LightGray),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
// Color Boxを描画
Box(
modifier = Modifier
.size(150.dp)
.background(
when (boxState) {
BoxState.Red -> Color.Red
BoxState.Green -> Color.Green
BoxState.Blue -> Color.Blue
}
)
.clickable {
boxState = when (boxState) {
BoxState.Red -> BoxState.Green
BoxState.Green -> BoxState.Blue
BoxState.Blue -> BoxState.Red
}
}
)
// Tapを支持するTextを描画
Text(text = "タップしてね", color = Color.White, fontSize = 20.sp)
}
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
SealedAppTheme {
ToggleBox()
}
}
ColorBoxをTapすると、sealed classと、mutableStateOfを使って、画面を更新して、Objectを切り替えることができます。
まとめ
今回は、Kotlinのsealed classで、ColorBoxを切り替えるロジックを作ってみました。クラスの継承を制限するんですね。他にも分岐処理のロジック作って試してみたいですね。
みんなも色々試してみてね。