I'm having a little problem understanding variance of methods when overloading.
While this perfectly works due to covariance in the return type
class Bla
class Fasel extends Bla
trait Test[A] {
def tester(): Bla = new Bla
}
class FooTest[A](a: A) extends Test[A] {
override def tester(): Fasel = new Fasel
}
this one fails even though functions are contravariant in their parameter types.
class Bla
class Fasel extends Bla
trait Test[A] {
def tester(a: Fasel): Bla = new Bla
}
class FooTest[A](a: A) extends Test[A] {
override def tester(a: Bla): Fasel = new Fasel
}
What am I getting wrong here? Any pointers?
Regards, raichoo
There are two things going on here:
Your tester
method is a method, not a Function1
. It can be lifted into a function using the underscore syntax:
val f = (new FooTest[String]).tester _ // Fasel => Bla
This function will be contra-variant in its input type. (It's worth saying, however, that functions cannot be parameterized and also worth saying that I had to have an instance of Foo
or FooTest
in order to get a function object for the tester
method. This of course follows from the first observation!)
A function is an object, it cannot be overridden as that makes no sense. Methods can be overridden. However, as I say above, the overriding is not polymorphic in the method's parameter types. So for example:
class A {
def foo(a : Any) = println("A: " + a)
}
class B extends A {
override def foo(s : String) = println("B " + s) //will not compile!
}
The two methods in my example above are two separate methods: dynamic dispatch works only on the method target (i.e. the object on which it is being called).
In the above, example, if you remove the override
declaration, the code will compile. If you run the following:
(new B).foo(1) //prints A 1
(new B).foo("s") //prints B s
This is because, although both methods are called foo
, they are completely different methods (i.e. I have overloaded foo
, not overridden it). It's best understood as being that a method's arguments' (incl their types) form part of that method's unique name. One method overrides another only if they have exactly the same name.
Essentially you have confused what are two separate and un-related things in your question, which I will put down for clarity:
Function1
define what it means for one function to be a subtype of another (and hence assignable to a reference of a given type).The relevant snippets of the spec:
Method Types
A method type is denoted internally as
(Ps)U
, where(Ps)
is a sequence of parameter names and types(p1 :T1,...,pn :Tn)
for somen≥0
andU
is a (value or method) type. This type represents named methods that take arguments namedp1, ..., pn
of typesT1,...,Tn
and that return a result of typeU
.Method types do not exist as types of values. If a method name is used as a value, its type is implicitly converted to a corresponding function type (§6.26).
Overriding
A member
M
of classC
that matches (§5.1.3) a non-private memberM′
of a base class ofC
is said to override that member. In this case the binding of the overriding memberM
must subsume (§3.5.2) the binding of the overridden memberM′
.
Conformance
If
Ti ≡ Ti′
fori = 1, ..., n
andU
conforms toU′
then the method type(p1 : T1,...,pn :Tn)U
conforms to(p1′ :T1′,...,pn′ :Tn′)U′
.
Subsumes
A declaration or definition in some compound type of class type
C
subsumes another declaration of the same name in some compound type or class typeC′
, if one of the following holds.
- A value declaration or definition that defines a name x with type T subsumes a value or method declaration that defines
x
with typeT′
, providedT <: T′
.
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