Kotlin如何将抽象值作为抽象函数的默认参数?
解决密封类子类数据类的自定义copy方法问题
你遇到的问题本质是抽象函数无法直接使用抽象属性作为默认参数——编译器在处理抽象函数定义时,无法确定item和balance的具体值,所以会提示未初始化错误。要实现你想要的“只传修改参数、复用原有值”的效果,可以按以下方式调整代码:
核心思路
- 抽象层只定义
copyScreenState的参数列表,不设置默认值 - 在每个数据类子类的实现中,为参数添加默认值,默认值直接引用当前实例的属性(
this.item/this.balance) - 对于有专属额外参数的子类,同样为专属参数添加默认值,保证复用原有属性
修改后的代码示例
sealed class ScreenState { abstract val item: DTOPurchasableItem abstract val balance: String? // 抽象函数不设置默认参数 abstract fun copyScreenState(item: DTOPurchasableItem, balance: String?): ScreenState // 普通子类:NotEnoughMoney data class NotEnoughMoney( override val item: DTOPurchasableItem, override val balance: String? ) : ScreenState() { // 实现时添加默认参数,引用当前实例的属性 override fun copyScreenState( item: DTOPurchasableItem = this.item, balance: String? = this.balance ): ScreenState = copy(item = item, balance = balance) } // 普通子类:ItemAlreadyOwned data class ItemAlreadyOwned( override val item: DTOPurchasableItem, override val balance: String? ) : ScreenState() { override fun copyScreenState( item: DTOPurchasableItem = this.item, balance: String? = this.balance ): ScreenState = copy(item = item, balance = balance) } // 带专属参数的子类示例:PurchasedSuccess data class PurchasedSuccess( override val item: DTOPurchasableItem, override val balance: String?, // 专属额外参数 val timestamp: Long ) : ScreenState() { // 实现时包含专属参数的默认值(如果需要支持修改专属参数,可重载方法) override fun copyScreenState( item: DTOPurchasableItem = this.item, balance: String? = this.balance ): ScreenState = copy(item = item, balance = balance, timestamp = this.timestamp) // 重载方法:支持修改专属参数 fun copyScreenState( item: DTOPurchasableItem = this.item, balance: String? = this.balance, timestamp: Long = this.timestamp ): ScreenState = copy(item = item, balance = balance, timestamp = timestamp) } }
调用示例
完全符合你想要的使用方式:
val screenState = ScreenState.NotEnoughMoney(item = generateItem(), balance = "2000") // 只修改balance,复用原有item val newState = screenState.copyScreenState(balance = "100") // 带专属参数的子类调用示例 val successState = ScreenState.PurchasedSuccess(generateItem(), "1500", System.currentTimeMillis()) // 只修改balance,复用item和timestamp val newSuccessState = successState.copyScreenState(balance = "1000") // 修改balance和timestamp,复用item val updatedSuccessState = successState.copyScreenState(balance = "800", timestamp = System.currentTimeMillis())
这样既保留了密封类的状态区分能力,又实现了数据类的便捷copy特性,同时支持子类扩展专属参数的场景。
内容的提问来源于stack exchange,提问作者Felipe Ribeiro R. Magalhaes
相关产品推荐
相关产品推荐

