Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Array<Number>: get and set Int values without casting

I am building a Matrix class and want to be able to store Numbers in a 2d Array.

var data: Array<Array<Number>> = Array(width, {Array(height, {0})})

This does not work because Array<Number> and Array<Int> are invariant. I can make it work by using Array<Array<out Number>>, but the Matrix will be immutable, and I don't want that...

Casting {0 as Int} makes the compiler error go away, but this does not seem like a good idea. I also want to do things like addition and I noticed that it's not possible to add Numbers:

var n: Number = 1
n + 1 // does not work

So how could I solve this problem? And why exactly can I not add two Numbers?

like image 669
Patric Avatar asked May 15 '26 20:05

Patric


1 Answers

Number is an abstract class and does not define anything for addition. And since there's no defined method for adding the numbers, you can't do numberInstane + otherNumberInstance. You can, however, create an operator function for it:

infix operator fun Number.plus(other: Number) : Number{
    return when (this) {
        is Double -> this + other.toDouble()
        is Int -> this + other.toInt()
        is Long -> this + other.toLong()
        is Float -> this + other.toFloat()
        is Short -> this + other.toShort()
        is Byte ->  this + other.toByte()
        else -> 0
    }
}

Note that this only applies to addition. The rest of the functions would follow the same pattern, but replacing the operator (here it's +) and the name of the function (here it's plus).


As mer msrd0's comment, the above would result in 1 + 1.5 being 2, because it rounds down. Kotlin supports adding number types across each other, which ends up with this slightly horrid solution:

infix operator fun Number.plus(other: Number) : Number{

    when {
        this is Double -> {
            return when(other){
                is Double -> this + other
                is Int -> this + other
                is Long -> this + other
                is Float -> this + other
                is Short -> this + other
                is Byte ->  this + other
                else -> 0
            }
        }
        this is Int -> {
            return when(other){
                is Double -> this + other
                is Int -> this + other
                is Long -> this + other
                is Float -> this + other
                is Short -> this + other
                is Byte ->  this + other
                else -> 0
            }
        }
        this is Long -> {
            return when(other){
                is Double -> this + other
                is Int -> this + other
                is Long -> this + other
                is Float -> this + other
                is Short -> this + other
                is Byte ->  this + other
                else -> 0
            }
        }
        this is Float -> {
            return when(other){
                is Double -> this + other
                is Int -> this + other
                is Long -> this + other
                is Float -> this + other
                is Short -> this + other
                is Byte ->  this + other
                else -> 0
            }
        }
        this is Short -> {
            return when(other){
                is Double -> this + other
                is Int -> this + other
                is Long -> this + other
                is Float -> this + other
                is Short -> this + other
                is Byte ->  this + other
                else -> 0
            }
        }
        this is Byte -> {
            return when(other){
                is Double -> this + other
                is Int -> this + other
                is Long -> this + other
                is Float -> this + other
                is Short -> this + other
                is Byte ->  this + other
                else -> 0
            }
        }
        else -> return 0
    }
}

The nested when-statement helps autocasting the values, which is necessary since Number isn't a specific known class. There might be a better solution though, but without knowing the specific type. The extension function is mostly just auto-casting based on the type, but there can't be a single variable because it would need to be defined as a Number to accept all the types, and since there are two vars where both need proper casting based on the passed type, it ends up being slightly messy.

like image 200
Zoe stands with Ukraine Avatar answered May 18 '26 14:05

Zoe stands with Ukraine