Kotlin中的密封类与常规接口

图片

根据Kotlin官方文档,密封类代表限定的类层次结构,它提供了对继承的更多控制。当一个类被标记为密封时,它可以有子类,但所有这些子类必须在与密封类本身相同的文件中声明。这使得你在编译时就能知道所有可能的子类。

另一方面,接口更加通用,用于定义类必须满足的约定。它们是一组函数(带有或不带有默认实现),实现接口的类必须提供这些函数。

那么,假设我们的应用将发出一个"UiState"类的StateFlow,并且我们希望限制子类为以下类型:Loading、Main、LoggedIn、LoggedOut、NavigateToUrl(这需要一个String类的参数)。我们应该选择密封类还是接口呢?(注意:我们将枚举排除在讨论之外,因为某些类型需要接受参数)

让我们探索两种选择:

密封类

sealed class UiState {
    object Loading : UiState()
    object Main: UiState()
    object LoggedIn : UiState()
    object LoggedOut : UiState()
    data class NavigateToUrl(val url: String) : UiState()
}
view raw

接口

interface UiState

object Loading : UiState
object Main : UiState
object LoggedIn : UiState
object LoggedOut : UiState
data class NavigateToUrl(val url: String) : UiState

在这个用例中,你可以看到密封类是明显的赢家。不仅它提供了所有子类必须在同一文件中声明的限制,而且它还提供了详尽的类型检查:当我们使用when表达式来检查一个密封类类型时,编译器知道所有可能的类型并确保所有类型都被覆盖。

fun handleUiState(uiState: UiState) {
    when (uiState) {
        is UiState.Loading -> {
            println("Loading state")
        }
        is UiState.Main -> {
            println("Main state")
        }
        is UiState.LoggedIn -> {
            println("Logged In state")
        }
        is UiState.LoggedOut -> {
            println("Logged Out state")
        }
        is UiState.NavigateToUrl -> {
            println("Navigate to URL: ${uiState.url}")
        }
    }
}

// Example usage
val currentState: UiState = UiState.NavigateToUrl("https://www.example.com")

handleUiState(currentState)

再来看一个例子,我们的应用API调用需要一个"RetryPolicy",它包含3个属性:numRetries、delayMillis、delayIntervalCoefficient。我们也想实现一个默认策略。哪种方式更好呢?

密封类

sealed class RetryPolicy {
    abstract val numRetries: Long
    abstract val delayMillis: Long
    abstract val delayIntervalCoefficient: Long

    data class DefaultRetryPolicy(
        override val numRetries: Long = 3,
        override val delayMillis: Long = 500,
        override val delayIntervalCoefficient: Long = 2
    ) : RetryPolicy()
}

接口

interface RetryPolicy {
    val numRetries: Long
    val delayMillis: Long
    val delayIntervalCoefficient: Long
}

data class DefaultRetryPolicy(
    override val numRetries: Long = 3,
    override val delayMillis: Long = 500,
    override val delayIntervalCoefficient: Long = 2
) : RetryPolicy

在这种情况下,接口是明显的赢家,因为我们不需要详尽地检查子类,相反,我们可能会创建许多"RetryPolicy"子类,它们可能会在不同的文件或模块中,取决于哪里需要使用它。我们关心的只是我们必须满足"RetryPolicy"类中定义的约定。

结论

那么何时使用密封类 versus 接口呢?

  • • 使用密封类:当你想表示一组固定的类型,并希望在编译时确保所有情况都被处理时(例如,用于在状态机中建模不同的状态)。

  • • 使用接口:当你想定义多个类必须遵守的约定,而不强制执行严格的层次结构时(例如,用于定义不同类之间的共同行为)。

 

转自:Kotlin中的密封类与常规接口

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值