Kotlin Type Safety Builder One Time Usage Record
- 2021-12-04 19:39:32
- OfStack
In the related application framework of android official guidance, one Resource class is used to represent the status and result of network request
// A generic class that contains data and status about loading this data.
sealed class Resource<T>(
val data: T? = null,
val message: String? = null
) {
class Success<T>(data: T) : Resource<T>(data)
class Loading<T>(data: T? = null) : Resource<T>(data)
class Error<T>(message: String, data: T? = null) : Resource<T>(data, message)
}
This is what we use in activity in most cases
private val testViewModel : TestViewModel by viewModels()
private fun getUserInfo(uid: String) {
testViewModel.userInfoData.observe(this, Observer {
when (it.status) {
Status.SUCCESS -> TODO()
Status.ERROR -> TODO()
Status.LOADING -> TODO()
}
})
testViewModel.setUserId(uid)
}
It's annoying to write too much. It's when () every time. Is there a better way to write it? Like this?
private fun getUserInfo(uid: String) {
testViewModel.userInfoData.observe(this, Observer {
success {
}
error {
}
loading {
}
})
testViewModel.setUserId(uid)
}
I can not write error/loading cases when I only need to deal with success.
kotlin's type-safe builder can do this. Let's take a look at the official example first
class HTML {
fun body() { ... }
}
fun html(init: HTML.() -> Unit): HTML {
val html = HTML() // Create a recipient object
html.init() // Pass the recipient object to the lambda
return html
}
html { // With receiver lambda Begin from this
body() // Object of the recipient object 1 Methods
}
First of all, what we need is an object that implements Observer interface.
So we first define a class to implement the Observer interface
class ResourceObserver<T: Any> : Observer<Resource<T>> {
override fun onChanged(t: Resource<T>) {
when(t) {
is Resource.Success -> TODO()
is Resource.Error -> TODO()
is Resource.Loading -> TODO()
}
}
}
Implement a top-level function and return an ResourceObserver object
fun <T: Any> resourceObserver(init: ResourceObserver<T>.() -> Unit): ResourceObserver<T> {
val observer = ResourceObserver<T>()
observer.init()
return observer
}
Call this function to get the ResourceObserver object
resourceObserver {
// Where you can call member functions within an object
}
So my implementation is
class ResourceObserver<T: Any> : Observer<Resource<T>> {
private var success: (Resource.Success<T>.() -> Unit)? = null
private var error: (Resource.Error.() -> Unit)? = null
private var loading: (Resource.Loading<T>.() -> Unit)? = null
fun success(s: (Resource.Success<T>.() -> Unit)) {
success = s
}
fun error(e: Resource.Error.() -> Unit) {
error = e
}
fun loading(l: Resource.Loading<T>.() -> Unit) {
loading = l
}
override fun onChanged(t: Resource<T>) {
when(t) {
is Resource.Success -> success?.invoke(t)
is Resource.Error -> error?.invoke(t)
is Resource.Loading -> loading?.invoke(t)
}
}
}
Summarize