Hey! I was wondering if it was possible to automat...
# getting-started
t
Hey! I was wondering if it was possible to automatically cast a sealed class to one of it's children if a certain condition is met.
Copy code
sealed class Response(val success: Boolean) {
    class Success : Response(true)
    class Failure : Response(false)
}
So from this I'd like to do this.
Copy code
// Example
val res = Response(true)

if(res.success) {
  // cast to Response.Success
} else {
  // cast to Response.Failure
}
I know I can probably just use the
is
clause but I think it'd be a tad bit nicer to do it like this, if it's not possible I'll just use
is
. Thanks in advance!
s
The closest I think you can get is this, using contracts:
Copy code
sealed interface Response {
    object Success: Response
    object Failure: Response
}

fun Response.isSuccess(): Boolean {
    contract { returns(true) implies(this@isSuccess is Response.Success) }
    return this is Response.Success
}
which is really just a long-winded way of doing the
is
check 😄
I think your example is flawed because
val res = Response(true)
won’t compile as
Response
is abstract
t
Yeah I know it wont compile, just wanted to write a quick example of what I'm trying to achieve
👍 1
Let me try out your solution, thank you!
Ah I just noticed you switched it to an interface, this is my actual code.
Copy code
sealed class ServiceResponse<out T>(
    val success: Boolean
) {
    @Contextual
    abstract val data: T?

    /**
     * Represent a successful API operation.
     * @param data Optional data to be passed along.
     */
    @Serializable(with = ServiceResponseSerializer::class)
    data class Success<T>(
        override val data: T? = null
    ) : ServiceResponse<T>(true)

    /**
     * Represent a failed API operation.
     * @param message Optional error message.
     * @param data Optional data to be passed along.
     */
    @Serializable(with = ServiceResponseSerializer::class)
    data class Failure<T>(
        val message: String? = null,
        override val data: T? = null
    ) : ServiceResponse<T>(false)
}
I do it like this so with my API I can just respond with
ServiceResponse.Success<Unit>()
and I also have an error handler to check if I send back a failure.
s
👍 the contract should still work if it’s a class, I just made it an interface since after I moved the
success
property out to a function it didn’t have any state any more
t
Get's a tad bit weird when we bring generics into the mix, think I'll just stick with the
is
check, but contracts seem really cool so I'll learn more about those later Thanks for showing it
e
even in the original design, there is no need to store
success
in a field:
Copy code
sealed class Response {
    abstract val isSuccess: Boolean

    class Success : Response() {
        override val isSuccess: Boolean
            get() = true
    }

    class Failure : Response() {
        override val isSuccess: Boolean
            get() = false
    }
}
the extension makes more sense to me though
t
I think I did it like that because of some issues with kotlinx-serialization Don't quite remember though