Kotlin中如何让data class参数支持Int或BigDecimal两种类型
Kotlin实现Money类支持Int/BigDecimal双类型入参的方案
方案1:密封接口实现(更推荐,类型安全、易扩展)
该方案通过密封接口约束公共能力,不同入参类型对应独立的实现类,完全解耦不同类型的toSmallestUnit逻辑,符合开闭原则。
import java.math.BigDecimal // 币种枚举示例 enum class CurrencyCode { USD, EUR } sealed interface Money { val currencyCode: CurrencyCode fun toSmallestUnit(): Int // Int类型入参的实现类 data class IntAmount( val amount: Int, override val currencyCode: CurrencyCode ) : Money { override fun toSmallestUnit(): Int = when (currencyCode) { CurrencyCode.USD -> amount * 100 CurrencyCode.EUR -> amount * 100 } } // BigDecimal类型入参的实现类 data class BigDecimalAmount( val amount: BigDecimal, override val currencyCode: CurrencyCode ) : Money { override fun toSmallestUnit(): Int = when (currencyCode) { CurrencyCode.USD -> amount.multiply(BigDecimal(100)).intValueExact() CurrencyCode.EUR -> amount.multiply(BigDecimal(100)).intValueExact() } } companion object { // 重载工厂方法,对外暴露统一的调用入口,和普通构造方法使用方式一致 operator fun invoke(amount: Int, currencyCode: CurrencyCode): Money = IntAmount(amount, currencyCode) operator fun invoke(amount: BigDecimal, currencyCode: CurrencyCode): Money = BigDecimalAmount(amount, currencyCode) } }
使用示例
// 两种入参类型都支持 val intMoney = Money(20, CurrencyCode.USD) val decimalMoney = Money(BigDecimal("20.55"), CurrencyCode.EUR) // 自动匹配对应类型的toSmallestUnit实现 println(intMoney.toSmallestUnit()) // 输出2000 println(decimalMoney.toSmallestUnit()) // 输出2055
方案2:单一DataClass实现(适合不需要扩展的轻量场景)
如果要求对外只暴露一个Money数据类,可以通过私有主构造+公开次构造重载实现,用私有字段标记入参类型,分支处理toSmallestUnit逻辑。
import java.math.BigDecimal enum class CurrencyCode { USD, EUR } data class Money private constructor( val amount: BigDecimal, val currencyCode: CurrencyCode, // 私有字段标记入参类型,外部不可访问 private val isIntInput: Boolean ) { // Int类型入参的构造方法 constructor(amount: Int, currencyCode: CurrencyCode) : this( amount = amount.toBigDecimal(), currencyCode = currencyCode, isIntInput = true ) // BigDecimal类型入参的构造方法 constructor(amount: BigDecimal, currencyCode: CurrencyCode) : this( amount = amount, currencyCode = currencyCode, isIntInput = false ) fun toSmallestUnit(): Int = if (isIntInput) { // Int入参对应的实现逻辑 when (currencyCode) { CurrencyCode.USD -> amount.intValueExact() * 100 CurrencyCode.EUR -> amount.intValueExact() * 100 } } else { // BigDecimal入参对应的实现逻辑 when (currencyCode) { CurrencyCode.USD -> amount.multiply(BigDecimal(100)).intValueExact() CurrencyCode.EUR -> amount.multiply(BigDecimal(100)).intValueExact() } } }
方案选择建议
- 优先选密封接口方案:后续如果要新增
Long、Double等其他入参类型,只需要新增实现类即可,不需要修改原有逻辑,也不存在分支判断遗漏的风险 - 仅当业务场景非常稳定,不会扩展其他入参类型时,选择单一DataClass方案
内容的提问来源于stack exchange,提问作者Leff
相关产品推荐
相关产品推荐

