I’m making a value class that I want to use to represent any number, integer or floating, in a particular unit. I want to make methods/operators on that class in the following way :
fun Metre.unaryPlus() = +value
The issue is that Number (in addition to being an abstract class instead of an interface for some reason) does not support unaryPlus or any other operator, despite all of its concrete implementations as the basic types, actually supporting them.
How can I fill the T in the following:
value class Second(public val value: T)
such that I may call unaryPlus, unaryMinus, plus, minus, times, div, rem, equals and compareTo on it (or between it and another T) ?
I would put Byte|Short|Int|Long|Float|Double but type unions aren’t a thing (is it allowed to cheer for Typescript here ?).
Note that you can’t do this for every concrete Number type, because there is an infinite number of possible classes that extend Number. For example, in addition to the primitive wrappers (Int, Double etc), there is BigDecimal and third-party libraries offering Rational etc.
That’s true, as you cannot anticipate how some custom Number type does addition etc. The Number interface is really pathetic in this regard, but there is no good solution for this, except if the Kotlin team decides to retrofit some richer interfaces into the existing hierarchy (similar to bringing MutableList etc into the collection mix).
If the operations are used in a lot of places, and if you want to make it easy to support custom types, the best you could do is to add a typeclass-like interface for arithmetic, e.g.
interface Arithmetic<T: Number> {
val zero: T
val one: T
operator fun T.plus(other: T): T
operator fun T.minus(other: T): T
operator fun T.times(other: T): T
}
Right now, this would be still cumbersome, but maybe this would be a good candidate for a context parameter (but I don’t know how well they work with generics).
It is not possible to abstract away number operations. What is the type of sum of Int and BigDecimal? The design of Number class was a mistake in Java. It should not be open for inheritance.