Inheritance in Scala (and in almost any other language) means subtyping relationship: if A extends B
, then everywhere where you can use B
, you can use A
. Every operation which can be applied to B
you can also apply to A
. For data structures this means that child contents are a superset of parent contents; operations can usually be dispatched virtually to allow specializing.
However, all of this does not make sense for Double
/Int
. While Double
does cover the whole range of Int
(because its mantissa is 53 bits, which is almost twice as long as the whole Int
), their internal representation is completely different and neither is the superset of the other.
This would probably be unimportant if numerical types used virtual dispatch. After all you could override +
/-
/... for Int
to behave differently, using Int
internal structure, couldn't you? However, this implies that every single one numeric value should carry a piece of metadata with it (a virtual table and a lot of JVM-related stuff), which will be larger than the value itself! Moreover, every single numerical operation should go through the virtual table instead of a simple assembly operation.
Another problem with virtual dispatch on numeric types is this:
def divide(x: Double, y: Double): Double {
x / y
}
let (a: Int, b: Int) = (1, 2)
assert(divide(a, b) == 0.5)
Naturally, you would expect that divide()
returned 0.5, right? However, with virtual dispatch it would return 0, because actual division is performed on Int
s with their own division method! And what happens if we mix Double
and Int
?
divide(1: Int, 2: Double) == ?
Which rules should be used here? Maybe some kind of automatic conversion should be applied?
You see, making Int
a subtype of Double
brings a lot of questions on how to do it properly and efficiently. Other approaches of unifying numerical types and operations on them usually do not base on classes and inheritance, but JVM is rather rigid in this regard.
What you really need depends on your actual program. The simplest way is just to return literal 42
from Double
method (it will be used as Double
automatically):
class IntFactory extends NumberFactory {
def createNumber(): Double = 42
}
Suggestion to use a type parameter is also valid for other use cases.