Kotlin-使多个密封类具有公共的“基”子类集,但每个类仍然可以添加其特定的子类

qhhrdooz  于 2022-12-23  发布在  Kotlin
关注(0)|答案(1)|浏览(92)

此问题的范围比Extract common objects from sealed class in kotlinAndroid - How to make sealed class extend other sealed class?更广,因此不是重复问题
我有多个密封类,它们表示各种API调用的结果,每个调用都有一组共同的预期结果(成功、网络错误、意外错误),但每个调用都可能引入自己的结果类型(如“找不到用户”或“错误ID”)。
为了避免将相同的子类复制到每个密封类中,我想创建一个包含所有公共结果类型的“基”类型,而每个密封类可以添加其特定的子类:

interface BaseApiCallResult {
    data class Success(val data: String) : BaseApiCallResult
    data class UnexpectedError(val error: Throwable) : BaseApiCallResult
    data class NetworkError(val error: ApolloException) : BaseApiCallResult
}

sealed class ApiCallResult1 : BaseApiCallResult {
    data class WrongID(val id: Int) : ApiCallResult1()
}

sealed class ApiCallResult2 : BaseApiCallResult {
    data class UserDoesNotExist(val userid: Long) : ApiCallResult2()
}

sealed class ApiCallResult3 : BaseApiCallResult {
    data class NameAlreadyTaken(val name: String) : ApiCallResult3()
}

问题是“基”中的子类不能被视为“子”类:

fun apiCall1(): ApiCallResult1 {
    // won't compile, since BaseApiCallResult.UnexpectedError is not ApiCallResult1
    return BaseApiCallResult.UnexpectedError(Exception(""))
}

fun useApi() {
        when(val result = apiCall1()) {
            is ApiCallResult1.WrongID -> {  }
            // compile error: Incompatible types
            is BaseApiCallResult.Success -> {  }
            is BaseApiCallResult.UnexpectedError -> {  }
            is BaseApiCallResult.NetworkError -> {  }
        }
    }

Android - How to make sealed class extend other sealed class?的解决方案可能适用于此处,但对于大量的密封类(我预计可能需要几十个这样的类),它变得相当笨拙

interface BaseApiCallResult {
    data class Success(val data: String) : Everything
    data class UnexpectedError(val error: Throwable) : Everything
    data class NetworkError(val error: ApolloException) : Everything
}

sealed interface ApiCallResult1 : BaseApiCallResult {
    data class WrongID(val id: Int) : ApiCallResult1()
}

sealed interface ApiCallResult2 : BaseApiCallResult {
    data class UserDoesNotExist(val userid: Long) : ApiCallResult2
}

sealed interface ApiCallResult3 : BaseApiCallResult {
    data class NameAlreadyTaken(val name: String) : ApiCallResult3
}

// adding each new sealed interface here seems like a hack
interface Everything : BaseApiCallResult, ApiCallResult1, ApiCallResult2, ApiCallResult3

另外,使用上面的解决方案,每个when {...}都会抱怨Everything的情况没有得到处理。我可以放弃使用Everything,但是我必须列出每个“基本”子类中的所有接口,这简直太可怕了:

// just imagine how would it look if there were 30 ApiCallResult classes
interface BaseApiCallResult {
    data class Success(val data: String) : BaseApiCallResult, ApiCallResult1, ApiCallResult2, ApiCallResult3
    data class UnexpectedError(val error: Throwable) : BaseApiCallResult, ApiCallResult1, ApiCallResult2, ApiCallResult3
    data class NetworkError(val error: ApolloException) : BaseApiCallResult, ApiCallResult1, ApiCallResult2, ApiCallResult3
}

有没有更好的办法来处理这种情况?

mzmfm0qo

mzmfm0qo1#

您必须将ApiResult与ApiMethodResult分开,它们不应是亲属。
Kotlin已经有了结果类型,您可以使用它:

sealed interface ApiCall1Result {
    class WrongID : ApiCall1Result
    class UserInfo(val userId: Int) : ApiCall1Result
}

fun api1() : Result<ApiCallResult>

fun useApi1() {
    val result = api1()
    if(result.isFailure) {
        handle failure
    } else {
        val apiResult = result.getOrThrow()
        when(apiResult) {
            is WrongID -> {}
            is UserInfo -> {}
        }
    }
}

或者您可以自行实现:

interface ApiResult<in T> {
    class Success<T : Any>(val data: T) : ApiResult<T>
    class Fail(val error: Throwable) : ApiResult<Any>
}

sealed class ApiCallResult1 {
    class WrongID(val id: Int) : ApiCallResult1()
    class UserInfo(val id: Int, val name: String) : ApiCallResult1()
}

fun apiCall1(): ApiResult<ApiCallResult1> {
    return ApiResult.Fail(Throwable())
}

fun useApi() {
    when (val result = apiCall1()) {
        is ApiResult.Fail -> {}
        is ApiResult.Success -> when (result.data) {
            is ApiCallResult1.WrongID -> {}
            is ApiCallResult1.UserInfo -> {}
        }
    }
}

相关问题