2021年6月1日火曜日

Kotlin の sealed interface が必要になる例

(本当は Compose のコード例(State/MutableState)にしたかったんだけど、まだ Jetpack Compose は Kotlin 1.5 に対応してないので sealed interface 使えないんだよね...)


ViewModel で持ってる LiveData を Activity で observe してるとする。
  1. class MainActivity : AppCompatActivity() {  
  2.   
  3.     private val viewModel: MainViewModel by viewModels()  
  4.   
  5.     override fun onCreate(savedInstanceState: Bundle?) {  
  6.         super.onCreate(savedInstanceState)  
  7.         setContentView(R.layout.activity_main)  
  8.   
  9.         viewModel.state.observe(this) {  
  10.             when (it) {  
  11.                 is State.Data -> {  
  12.                     println(it.value)  
  13.                 }  
  14.                 State.Loading -> {  
  15.   
  16.                 }  
  17.             }  
  18.         }  
  19.     }  
  20. }  
  1. class MainViewModel : ViewModel() {  
  2.   
  3.     private val _state = MutableLiveData<State>()  
  4.     val state: LiveData<State>  
  5.         get() = _state  
  6.   
  7.     fun doSomething() {  
  8.         val state = _state.value  
  9.         if (state is State.Data) {  
  10.             state.mutableValue = Random.nextInt()  
  11.         }  
  12.     }  
  13. }  
  14.   
  15. sealed class State {  
  16.     object Loading : State()  
  17.   
  18.     data class Data(val id: String) : State() {  
  19.         // 変更できるのは MainViewModel からだけにしたいが、  
  20.         // private にすると MainViewModel からも見えなくなる  
  21.         var mutableValue: Int = -1  
  22.   
  23.         val value: Int  
  24.             get() = mutableValue  
  25.     }  
  26. }  
↑ State.Data が持つ mutableValue は MainViewModel からのみ変更できるようにしたい。

mutableValue に private は MainViewModel から見えなくなるのでダメ。
  1. private var mutableValue: Int = -1  
これもダメ。
  1. private sealed class State {  
  1. private data class Data(val id: String) : State() {  
Data を top level にすると private をつけても MainViewModel から見えるけど、MainActivity から見えなくなるのでダメ。
  1. sealed class State  
  2.   
  3. object Loading : State()  
  4.   
  5. // MainViewModel から mutableValue は見えるが  
  6. // Data が MainActivity からは見えなくなる  
  7. private data class Data(val id: String) : State() {  
  8.     var mutableValue: Int = -1  
  9.   
  10.     val value: Int  
  11.         get() = mutableValue  
  12. }  


ということで sealed interface を使います。
  1. class MainViewModel : ViewModel() {  
  2.   
  3.     private val _state = MutableLiveData<State>()  
  4.     val state: LiveData<State>  
  5.         get() = _state  
  6.   
  7.     fun doSomething() {  
  8.         val state = _state.value  
  9.         if (state is DataImpl) {  
  10.             state.mutableValue = Random.nextInt()  
  11.         }  
  12.     }  
  13. }  
  14.   
  15. sealed interface State  
  16.   
  17. object Loading : State  
  18.   
  19. sealed interface Data : State {  
  20.     val value: Int  
  21. }  
  22.   
  23. private class DataImpl(val id: Int) : Data {  
  24.     // private class なので変更できるのは同じファイルからだけ  
  25.     var mutableValue: Int = id  
  26.   
  27.     override val value: Int  
  28.         get() = mutableValue  
  29. }  


0 件のコメント:

コメントを投稿