How to override a generic method with implicit arguments if the generic type is already fixed?
I try to override this method
def sum[B >: A](implicit num: Numeric[B]): B = ...
in a subclass where type A
is already fixed to Int
.
I already tried
override def sum: Int = ...
but this doesn't override of course, leading to different method resolution based on the dynamic type at runtime.
Going further,
def sum[B >: Int](implicit num: Numeric[B]): Int
does override, while
def sum[B >: Int](implicit num: Numeric[Int]): Int
does not, as well as
def sum(implicit num: Numeric[Int]): Int
Why is that the case? Is it at leats possible to get rid of the superfluous bound B
?
I'm not sure which types and implicits I can leave out and what has to stay so that the method still o开发者_StackOverflowverrides.
The first problem is that overridden methods need the same number and kind of type parameters, even if they are not used. For example,
class C1 {
def f[B] = println("hello")
}
class C2 extends C1 {
override def f = println("world") // Error: f overrides nothing (needs parameter B)
}
Beyond this, there's also a problem with soundness since Numeric[A]
is invariant in A
. This means there is no subtype relationship between Numeric[B]
and Numeric[C]
whenever B
and C
are different.
Imagining that Numeric
were a trait with the proper variance, it still wouldn't work; apparently, the signature of overridden methods needs to be exactly the same:
class D1 {
def g(x: Int) {}
}
class D2 extends D1 {
override def g(x: Any) {} // Error: g overrides nothing (different parameter type)
}
I'm not sure why the types of overridden methods can't be widened. Edit: Perhaps the reason is compatibility with overloading, as in
class D1 {
def g(x: Any) {}
def g(x: Int) {} // This is legal, even though the definitions seem to overlap
}
To summarize, when you override, you must preserve the signature of the method, including the type parameters. In your case, this is about the best you can do:
override def sum[B >: Int](implicit num: Numeric[B]): B = ...
Ok, trying to explain why the rules must force you to keep the signature with implicit parameter and variance.
First, animplicit argument is still an argument, it can be passed explicitely, and except maybe when it has a singleton type (which would not be very useful), several different instances of it are possible.
Suppose I create
case class ZModulo(val p: Int) extends Numeric[Int] {
def plus(a: Int, b: Int) = (a+b) % p
// others along the same line
}
It seems like a proper Numeric
. Numeric
documentation does not say which laws should be expected, but ZModulo
is not unreasonable.
Now there is your
class Summable[A] {
def sum[B >: A](implicit num: Numeric[A]): B =...
}
If I have val ints : Summable[Int]
, I am certainly allowed to call ints.Sum(ZModulo(3))
. So if your class is to be a subclass of Summable[Int]
, it must allow me that. So you cannot remove the Numeric
parameter.
Second, suppose I come with a Numeric[Any]
. Not sure how I could do that reasonably for a numeric, but the spec and the compiler can't know that. And anyway, they must accept unreasonable implementations too. So let's have
object MixThemAll : Numeric[A] {...}
The signature in Summable allows ints.sum(MixThemAll)
. So your subclass must allow that too.
So letting you remove either implicit parameter or variance in the subclass would be unsound.
精彩评论