How to add generic Throwable on a method signature - kotlin

How can I create a Map (it that's the best way) of having something for instance :
400 to CustomExceptionFor400
500 to CustomExceptionFor500
The first param is an Int and it's a HttpStatusCode, and the value is something like this
sealed class OrganizationExceptions : Exception() {
object OrganizationNotFound : OrganizationExceptions()
object ListNotAvailable : OrganizationExceptions()
}
This is an example, but it won't be always OrganizationExceptions I'm creating a method generic, also I don't know if it's better to create a sealed class or create
class OrganizationNotFoundException : Throwable() //or Exception()
class ListNotAvailable : Throwable() //or Exception()
Any recomendations?
the method signature is :
fun apiCallWithStatusCode(codes : HashMap<Int, Throwable>, apiCall : suspend () -> Response<T>,):Result<Unit>{...}
So my method should return a Result so it means that if the HttpStatusCode is 400 I should return return Result.failure(CustomExceptionFor400) that is the one that should come in the codes from the method.
PSEUDO EXAMPLE OF WHAT I WOULD LIKE TO HAVE
suspend fun <T : Throwable> apiCallWithStatusCode(
codes: Map<Int, T>,
apiCall: suspend () -> Response<T>,
): Result<Unit> {
runCatching { apiCall() }
.fold(
onSuccess = { response ->
if (response.isSuccessful) Result.success(Unit)
return codes[response.code()]?.let {
Result.failure(it)
} ?: Result.failure(GeneralError)
},
onFailure = {
return when (it) {
//Return Result.failure(WhateverError) this is done already
}
}
)
}
But I don't know if that's the way to do it.
My questions are :
What's better
sealed class OrganizationExceptions : Exception() {
object OrganizationNotFound : OrganizationExceptions()
object ListNotAvailable : OrganizationExceptions()
}
Or
class OrganizationNotFoundException : Throwable() //or Exception()
class ListNotAvailable : Throwable() //or Exception()
Then after knowing this, is to create this generic function because now there are two "OrganizationNotFoundException" and "ListNotAvailable" but perhaps other feature of my app have different Exceptions so that's why I want to have a generic one.

Related

How can I know which the subclass of sealed class will return when I use Compose in Android Studio?

The Result<out R> is a sealed class which hold three subclass Success, Error and Loading.
The fun Greeting is #Composable.
By my design, I define queryList as Result class, and it is assigned as Loading first, then it will be Success or Error.
1: But the following code can't be compiled as the following error information, what's wrong with my Code?
2: Is there a better solution for my design?
Compile error
Property delegate must have a 'getValue(Nothing?, KProperty>)' method. None of the following functions are suitable.*
#Composable
fun Greeting(
name: String,
mViewMode:SoundViewModel= viewModel()
) {
Column() {
//The following code cause error.
val queryList by produceState(initialValue = Result<Flow<List<MRecord>>>.Loading ) {
value = mViewMode.listRecord()
}
when (queryList){
is Loading -> { ...}
is Error -> { ...}
is Success -> {...}
}
}
}
class SoundViewModel #Inject constructor(): ViewModel()
{
fun listRecord(): Result<Flow<List<MRecord>>>{
return aSoundMeter.listRecord()
}
}
sealed class Result<out R> {
data class Success<out T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
object Loading : Result<Nothing>()
}
Since queryList is backed by a delegate, it can not be final.
This means in theory, each time you access it, it might hold a different value. The kotlin compiler is very pessimistic about this and assumes that between the time the is Result.Success branch of your when statement is selected and val mydata = queryList.data is executed, the value of queryList might have changed.
To solve this, you can assign the current value of queryList to a final variable and work with that one instead:
when (val currentList = queryList) {
is Result.Error -> {}
is Result.Loading -> {}
is Result.Success -> {
SomeComposable(currentList.data) //currentList is properly smart-cast to Result.Success
}
}

Generics in Objects

I have a question about sealed class, generics and object.
Let's say I would like to model something like 3 finite cases with a sealed class something like this:
sealed class ChangeState<S> {
fun reduceState(state: S): S
}
data class SetState<S>(val newState: S) : ChangeState<S>() {
override fun reduce(state: S): S = newState
}
object NoStateChange : ChangeState<Nothing>() { // What do I specify here for ChangeState? Nothing?
override fun reduce(state: Nothing): Nothing {
throw Exception("This should never be called")
}
}
The goal is to provide a convenient way to define NoStateChange in a generic way that it can be used as following:
fun foo(i : Int) : ChangeState<Int> {
return if (i==0)
NoStateChange // Won't compile because return type is ChangeState<Nothing> but expected ChangeState<Int>
else
SetState(i)
}
Is there a way to do that with object and Generics somehow?
As pointed out by #Tenfour04 the issue is that out is needed but reduceState() would require in as well. However, reduceState() can be refactored out of the class hierarchy and moved to an extension function like that:
sealed class ChangeState<out S>
data class SetState<S>(val newState: S) : ChangeState<S>()
object NoStateChange : ChangeState<Nothing>()
fun <S> ChangeState<S>.reduce(state: S): S {
return when (val change = this) {
is SetState -> change.newState
is NoStateChange -> state
}
}

Kotlin secondary constructor with generic type

In java
I can achieve two constructors like
public TargetTitleEntryController() { }
public <T extends Controller & TargetTitleEntryControllerListener> TargetTitleEntryController(T targetController) {
setTargetController(targetController);
}
I want to convert it to Kotlin
class TargetTitleEntryController ()
with the secondary constructor. I don't know how to declare with generic type like Java counterpart.
There is no intersection types in Kotlin (sad)
But there is Generic constraints (hope)
But Generic constraints not applicable in the secondary constructor (sad)
But you can simulate secondary constructor in a companion object using Invoke operator overloading (workaround):
class TargetTitleEntryController {
// ...
companion object {
operator fun <T> invoke(targetController: T): TargetTitleEntryController
where T : Controller,
T : TargetTitleEntryControllerListener {
return TargetTitleEntryController().apply {
setTargetController(targetController)
}
}
}
}
Here is an example where you specify a Type T which implements two interfaces (CharSequence, Runnable):
class Person<T>(val name: String) where T : CharSequence, T : Runnable {
constructor(name: String, parent: T) : this(name) {
}
}
So actually something like this should work:
class TargetTitleEntryController<T> () where T : Controller, T : TargetTitleEntryControllerListener {
constructor(targetController: T) : this() {
}
}
You can do it like this :)
class TargetTitleEntryController <T>() : Controller() where T: Controller, T: TargetTitleEntryControllerListener<T> {
constructor(target: T) : this() {
targetController = target
}
}
you can implement it in your parent controller like this:
class TargetDisplayController : Controller(), TargetTitleEntryControllerListener<TargetDisplayController> {
var targetTitleEntryController = TargetTitleEntryController(this)
override fun onTitlePicked(String option) {
}
override fun onAttach(view: View) {
// push controller here
}
}

How to make sealed classes generic in kotlin?

Is it possible to use the AsyncResult class below to prevent redefining InFlight, Error and InFlight in UserDataAppResult and CreateUserResult?
//TODO: use this to make the below classes generic?
sealed class AsyncResult{
object InFlight : AsyncResult()
data class Error(val errorMessage: String) : AsyncResult()
data class Loaded<out T>(val users: T) : AsyncResult()
}
sealed class UserDataAppResult : AppResult() {
object InFlight : UserDataAppResult()
data class Error(val errorMessage: String) : UserDataAppResult()
data class Loaded(val users: List<User>) : UserDataAppResult()
}
sealed class CreateUserResult : AppResult() {
object InFlight : CreateUserResult()
data class Error(val errorMessage: String) : CreateUserResult()
data class Loaded(val users: User) : CreateUserResult()
}
Is it possible for the above code to look like this?
sealed class AsyncResult{
class InFlight : AsyncResult()
data class Error(val errorMessage: String) : AsyncResult()
data class Loaded<out T>(val users: T) : AsyncResult()
}
sealed class UserDataAppResult : AsyncResult()
sealed class CreateUserResult : AppResult()
val activeUsers: Flowable<UserDataAppResult> = appDatabase.userDao().getActiveUsers(appSettings.currentLanguage.ordinal)
.map<UserDataAppResult> { UserDataAppResult.Loaded(it) }
.onErrorReturn { UserDataAppResult.Error(it.localizedMessage) }
.startWith(UserDataAppResult.InFlight)
.observeOn(AndroidSchedulers.mainThread())
.share()
fun createUser(): Flowable<CreateUserResult> {
val userId = UUID.randomUUID().toString()
val user = User()
user.id = userId
return appDatabase.userDao().insertAll(user)
.map <CreateUserResult> { CreateUserResult.Loaded(user) }
.onErrorReturn { CreateUserResult.Error(it.localizedMessage) }
.startWith(CreateUserResult.InFlight)
}
Currently UserDataAppResult.Error is not found which makes sense.
But is it possible to reuse the AppResult sealed class hierarchy and introduce new types.
your Object can't have a generic type in Kotlin but this could be solved simply by following the example below:
sealed class ResponseState<out T> {
object Loading : ResponseState<Nothing>()
data class Error(val throwable: Throwable) : ResponseState<Nothing>()
data class Success<T>(val item: T) : ResponseState<T>()
}
writing:
val _state = MutableLiveData<ResponseState<MessageModle>>()
_state.postValue(ResponseState.Loading)
myNetworkCall { response, e
if (e != null) _state.postValue(ResponseState.Error(e))
else _state.postValue(ResponseState.Success(response))
}
reading:
state.observe(..., {state ->
when(state) {
Loading -> showLoading()
is Error -> showError(state.throwable)
is Success -> onSuccess(state.item)
}
}
It's not possible in Kotlin. Every type you use must have an explicitly declared class somewhere. Classes are not created implicitly by the compiler even in the case when nested classes are declared in the superclass.
For your problem, I recommend you rewrite the code from combining two inheritance-based hierarchies to one of the two combining inheritance and composition, or just restructure the hierarchy in some way, for example (I suppose the exact instance of a result would be irrelevant to you in case when it's not Loaded):
sealed class AsyncResult {
object InFlight : AsyncResult()
data class Error(val errorMessage: String) : AsyncResult()
sealed class Loaded<out T>(val result: T) : AsyncResult() {
sealed class UserDataAppResult(users: List<User>) : Loaded<List<User>>(users)
sealed class CreateUserResult(user: User) : Loaded<User>(user)
}
}
Via the Google Guidelines: https://developer.android.com/jetpack/docs/guide
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, var refreshing: Boolean = false) : Resource<T>(data)
class Error<T>(data: T? = null, message: String) : Resource<T>(data, message)
}
Inspired by solution from #kosh
-> ViewState:
sealed class ViewState<out T> {
object Loading : ViewState<Nothing>()
data class Error(val throwable: Throwable) : ViewState<Nothing>()
data class Success<T>(val item: T) : ViewState<T>()
}
-> inside ViewModel:
private val _homeVS = MutableLiveData<ViewState<HomeMode>>()
val homeVS: LiveData<ViewState<HomeMode>> get() = _homeVS
// start requesting API
_homeVS.value = ViewState.Loading
try {
val result = loadData()
_homeVS.value = ViewState.Success(result)
} catch (e: Exception) {
_homeVS.value = ViewState.Error(e)
}
Then you can use this generic in layout/View
-> View:
viewModel.homeVS.observe(viewLifecycleOwner, {state ->
when(state) {
is ViewState.Error -> showError(state.throwable)
is ViewState.Success -> onSuccess(state.item)
ViewState.Loading -> showLoading()
}
})
-> On layout we may need little mor tuning
sealed class ViewState<out T> {
object Loading : ViewState<Nothing>()
data class Error(val throwable: Throwable) : ViewState<Nothing>()
data class Success<T>(val item: T) : ViewState<T>()
fun toData(): T? = (this as? Success)?.item
}
toData provides data only onSuccess
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='#{vm.homeVS.toData() != null ? vm.homeVS.toData().param1 : ""}' />
<!--onLoading-->
<View
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility='#{vm.homeVS instanceof ViewState.Loading ? View.VISIBLE : View.GONE}' />
<!--onError-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility='#{vm.homeVS instanceof ViewState.Error ? View.VISIBLE : View.GONE}' />
Surely with BindingAdapter, you can make it even better. Here just for illustrating solution.
Good luck,'.

Kotlin - How to find and cast an element by its type

I have a collection of objects which inherit Component and I want a function which finds an object by its concrete class and return it.
But Kotlin does not like the cast I do, and adding #Suppress("UNCHECKED_CAST") is ugly.
I have the following code:
open class GameObjectImpl : GameObject {
private val attachedComponents = mutableSetOf<Component>()
#Suppress("UNCHECKED_CAST")
override fun <TComponent : Component> getComponent(type: KClass<TComponent>): TComponent? {
return attachedComponents.find { type.isInstance(it) } as? TComponent
}
}
This should work for you:
open class GameObjectImpl : GameObject {
val attachedComponents = mutableSetOf<Component>()
override inline fun <reified TComponent : Component> getComponent(type: KClass<TComponent>): TComponent? {
return attachedComponents.filterIsInstance<TComponent>().firstOrNull()
}
}