Home > other >  Combining When clauses gives me weird error in Kotlin
Combining When clauses gives me weird error in Kotlin

Time:04-20

Kotlin Code below works fine

 private inline fun <reified T> parseResponse(response: Any): Response<T> =
        when (response) {
            is T -> { Response(success = response, error = null) }
            is GetUserInfoResponseResult.Error -> {
                Response(
                    success = null,
                    error = Error(response.error.message ?: "", response.error.code?.name ?: "")
                )
            }
            is SetUserInfoResponseResult.Error -> {
                Response(
                    success = null,
                    error = Error(response.error.message ?: "", response.error.code?.name ?: "")
                )
            }
            else -> throw exception("Failed to process response ")
        }

However when I try to combine 2 when clauses

 private inline fun <reified T> parseResponse(response: Any): Response<T> =
        when (response) {
            is T -> { Response(success = response, error = null) }
            is GetUserInfoResponseResult.Error, SetUserInfoResponseResult.Error -> {
                Response(
                    success = null,
                    error = Error(response.error.message ?: "", response.error.code?.name ?: "")
                )
            }
         
            else -> throw exception("Failed to process response ")
        }

In intelliJ I got a red underline for Error in SetuUserInfoResponseResult.Error saying

classifier Error does not have a companion object and thus must be initialized here 

definitions

sealed class GetUserInfoResponseResult {

    data class Error(val error: GetUserInfoErrorResponse) : GetUserInfoResponseResult()

    data class Success(val success: GetUserInfoSuccessResponse) : GetUserInfoResponseResult()

}

sealed class SetUserInfoResponseResult {

    data class Error(val error: SetUserInfoErrorResponse) : SetUserInfoResponseResult()

    object Success : SetUserInfoResponseResult()

}

CodePudding user response:

Your current when clause checks whether response is an instance of GetUserInfoResponseResult.Error, or response is equal to the companion object of SetUserInfoResponseResult.Error. The second half is an equality check, because you didn't write is before it. SetUserInfoResponseResult.Error does not have a companion object, hence the error.

Adding the is doesn't fix everything. There are still errors where you try to access response.error.message. This is because smart casts only works when you are checking for a single type. Since you are checking for 2, the compile-time type of response is not smart casted, and is still Any.

You can fix this by introducing a common interface that both error types implements:

interface UserInfoErrorResponseResult {
    val error: UserInfoErrorResponse
}

interface UserInfoErrorResponse {
    val message: String?
    val code: ErrorCode? // I assume you have an ErrorCode class like this
}

Then you can just check for one type - is UserInfoErrorResponseResult - and smart cast would work:

 private inline fun <reified T> parseResponse(response: Any): Response<T> =
        when (response) {
            is T -> { Response(success = response, error = null) }
            is UserInfoErrorResponseResult -> {
                Response(
                    success = null,
                    error = Error(response.error.message ?: "", response.error.code?.name ?: "")
                )
            }
         
            else -> throw exception("Failed to process response ")
        }

Here's an example of how your classes would look like, implementing those interfaces, with some assumptions of how GetUserInfoErrorResponse and SetUserInfoErrorResponse look like.

data class GetUserInfoErrorResponse(
    override val message: String?,
    override val code: ErrorCode?
): UserInfoErrorResponse

data class SetUserInfoErrorResponse(
    override val message: String?,
    override val code: ErrorCode?
): UserInfoErrorResponse

sealed class GetUserInfoResponseResult {

    data class Error(override val error: GetUserInfoErrorResponse) :
        GetUserInfoResponseResult(), UserInfoErrorResponseResult

    data class Success(val success: GetUserInfoSuccessResponse) : GetUserInfoResponseResult()

}

sealed class SetUserInfoResponseResult {

    data class Error(override val error: SetUserInfoErrorResponse) :
        SetUserInfoResponseResult(), UserInfoErrorResponseResult

    object Success : SetUserInfoResponseResult()

}

Basically, you should just add override to everything the interface needs, and it should work.

  • Related