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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With