Home > Software design >  Kotlin - data class generics that can accept one of the 2 types for one of the paramaters
Kotlin - data class generics that can accept one of the 2 types for one of the paramaters

Time:10-15

I would like to achieve something where I could call a data class with either an Int or BigDecimal type. So for example:

data class Money(val amount: Int, val currencyCode: CurrencyCode) {

  fun toSmallestUnit(): Int = when (this.currencyCode) {
    CurrencyCode.USD -> this.amount * 100
    CurrencyCode.EUR -> this.amount * 100
  }
}

Here I would like to be able to send both Int and BigDecimal as an amount to data class Money, and then have different toSmallestUnit function for each type. How can I achieve this in Kotlin?

CodePudding user response:

One way to do it is like this

data class Money<T>(val amount: T, val currencyCode: CurrencyCode) {
    fun toSmallestUnit(): T = when(amount) {
        is Int -> {
            when (this.currencyCode) {
                CurrencyCode.USD -> (this.amount * 100) as T
                CurrencyCode.EUR -> (this.amount * 100) as T
            }
        }
        is BigInteger -> {
            when (this.currencyCode) {
                CurrencyCode.USD -> this.amount.multiply(BigInteger("100")) as T
                CurrencyCode.EUR -> this.amount.multiply(BigInteger("100")) as T
            }
        }
        else -> {
            amount
        }
    }
}

toSmallestUnit will return the right result type depending on the type. But as you can see it then does allow to create Money of any type

CodePudding user response:

Every time you use this class in code, the compiler has to know which type amount is for you to be able to do anything with it. If we were working with your own classes, you could define an interface that they both implement, but this is not possible with Int/BigDecimal.

So, I propose you design your class to simply use BigDecimal as the type of amount, and provide constructors and properties to allow it to convert from Int.

This is just an example. I'm not sure how it makes sense to convert a currency in BigDecimal to an integer.

data class Money(val amount: BigDecimal, val currencyCode: CurrencyCode) {
    
    constructor(amount: Int, currencyCode: CurrencyCode): this(amount.toBigDecimal(), currencyCode)
    
    val intAmount: Int get() = amount.toInt()

    fun toSmallestUnit(): Int = when (this.currencyCode) {
        CurrencyCode.USD -> intAmount * 100
        CurrencyCode.EUR -> intAmount * 100
    }
}
  • Related