Reputation: 1637
I am building a Matrix class and want to be able to store Number
s 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 Number
s:
var n: Number = 1
n + 1 // does not work
So how could I solve this problem? And why exactly can I not add two Number
s?
Upvotes: 3
Views: 69
Reputation: 28288
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.
Upvotes: 2