This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Why is it not allowed to override methods with contravariant parameter types?

7 replies
Sebastien Bocq
Joined: 2008-12-18,
User offline. Last seen 42 years 45 weeks ago.
This compiles:

var f:String => Object = {a => a}
var g:Object => String = {a => a.toString}
f = g

but this doesn't:

class X {
  def f(s:String):Object = s
}

class Y extends X {
  override def f(s:Object):String = s.toString // Method f overrides nothing
}

Why have this limitation in Scala? Is that a JVM issue?

Thanks,
Sébastien
H-star Development
Joined: 2010-04-14,
User offline. Last seen 2 years 26 weeks ago.
Re: Why is it not allowed to override methods with contravarian
simple answer: because it is another method that just happens to have the same name.
do you have an example where what you are trying to so makes sense?

Am 30.08.2010 10:21, schrieb Sébastien Bocq:
AANLkTikA208hNO3270icLH_atVb1yCngbz1_rqytQCnW [at] mail [dot] gmail [dot] com" type="cite">This compiles:

var f:String => Object = {a => a}
var g:Object => String = {a => a.toString}
f = g

but this doesn't:

class X {
  def f(s:String):Object = s
}

class Y extends X {
  override def f(s:Object):String = s.toString // Method f overrides nothing
}

Why have this limitation in Scala? Is that a JVM issue?

Thanks,
Sébastien

Kevin Wright 2
Joined: 2010-05-30,
User offline. Last seen 26 weeks 4 days ago.
Re: Why is it not allowed to override methods with contravarian
I do feel your pain here, as an Object=>String obviously conforms to the String=>Object signature
(i.e. I want a function that accepts a string, and a function accepting any object clearly does *that*.  I also want my function to return an object, and a string is-an object, so returning a String is perfectly acceptable)
It's a JVM restriction.  In order to override, the function signature must match exactly.
The only exception to this is that you can be covariant in the return type (as with Java).   But this requires some trickery behind the scenes...  As return types are considered part of the signature, the compiler must generate a forwarder method whenever you override with a covariant return.
And that's your answer, duplicate the trickery by hand!
in Y you must define:  override def f(s:String):Object and have it delegate the call to   def f(s:Object):String

On 30 August 2010 09:21, Sébastien Bocq <sebastien.bocq@gmail.com> wrote:
This compiles:

var f:String => Object = {a => a}
var g:Object => String = {a => a.toString}
f = g

but this doesn't:

class X {
  def f(s:String):Object = s
}

class Y extends X {
  override def f(s:Object):String = s.toString // Method f overrides nothing
}

Why have this limitation in Scala? Is that a JVM issue?

Thanks,
Sébastien



--
Kevin Wright

mail/google talk: kev.lee.wright@gmail.com
wave: kev.lee.wright@googlewave.com
skype: kev.lee.wright
twitter: @thecoda

Aydjen
Joined: 2009-08-21,
User offline. Last seen 1 year 28 weeks ago.
Re: Why is it not allowed to override methods with contravaria

Kevin Wright wrote:
> The only exception to this is that you can be covariant in the return
> type (as with Java).   But this requires some trickery behind the
> scenes...  As return types are considered part of the signature, the
> compiler must generate a forwarder method whenever you override with a
> covariant return.

Is that a JVM thing? As far as I understand the Java language spec, the
return type is not part of the method signature.

Aydjen
Joined: 2009-08-21,
User offline. Last seen 1 year 28 weeks ago.
Re: Why is it not allowed to override methods with contravaria

Sébastien Bocq wrote:
> class X {
>   def f(s:String):Object = s
> }
>
> class Y extends X {
>   override def f(s:Object):String = s.toString // Method f overrides
> nothing
> }
>
> Why have this limitation in Scala? Is that a JVM issue?

Suppose you would not have this limitation. In this situation:

class Parent {
def f(x: String) = "42"
def f(x: Int) = 42
}

class Child extends Parent {
override def f(x: Any) = 42
}

which superclass method would it override, the String or the Int one?

Kevin Wright 2
Joined: 2010-05-30,
User offline. Last seen 26 weeks 4 days ago.
Re: Why is it not allowed to override methods with contravarian
Yes, it's a JVM thing.
One commonly used trick by obfuscators is to overload on just return type, it makes decompiling fun :)

On 30 August 2010 11:14, Andreas Flierl <andreas@flierl.eu> wrote:

Kevin Wright <kev.lee.wright@gmail.com> wrote:
> The only exception to this is that you can be covariant in the return
> type (as with Java).   But this requires some trickery behind the
> scenes...  As return types are considered part of the signature, the
> compiler must generate a forwarder method whenever you override with a
> covariant return.

Is that a JVM thing? As far as I understand the Java language spec, the
return type is not part of the method signature.



--
Kevin Wright

mail/google talk: kev.lee.wright@gmail.com
wave: kev.lee.wright@googlewave.com
skype: kev.lee.wright
twitter: @thecoda

H-star Development
Joined: 2010-04-14,
User offline. Last seen 2 years 26 weeks ago.
Re: Why is it not allowed to override methods with contravaria

both at the same time :D

Am 30.08.2010 12:51, schrieb Andreas Flierl:
> Sébastien Bocq wrote:
>> class X {
>> def f(s:String):Object = s
>> }
>>
>> class Y extends X {
>> override def f(s:Object):String = s.toString // Method f overrides
>> nothing
>> }
>>
>> Why have this limitation in Scala? Is that a JVM issue?
> Suppose you would not have this limitation. In this situation:
>
> class Parent {
> def f(x: String) = "42"
> def f(x: Int) = 42
> }
>
> class Child extends Parent {
> override def f(x: Any) = 42
> }
>
> which superclass method would it override, the String or the Int one?
>
>
>

Sebastien Bocq
Joined: 2008-12-18,
User offline. Last seen 42 years 45 weeks ago.
Re: Why is it not allowed to override methods with contravarian
I noticed it when I tried to make an Option as an Iterable (c.f. below). I didn't understood why type inference was not working properly and then wondered why the compiler did not complained because map and flatMap where already defined in one of the parent.

Now I know why.
Thanks you all for the answers.

sealed abstract class Opt[+A] extends Iterable[A]
case object Non extends Opt[Nothing] {
    def iterator: Iterator[Nothing] = Iterator.empty
    def map[B](f:Nothing => B):Opt[B] = Non
    def flatMap[B](f:Nothing => Opt[B]):Opt[B] = Non
    override def filter(p:Nothing => Boolean):Opt[Nothing] = Non
}
case class Som[+A](a:A) extends Opt[A] {
    def iterator: Iterator[A] = Iterator.single(a)
    def map[B](f:A => B):Opt[B] = Som(f(a))
    def flatMap[B](f:A => Opt[B]):Opt[B] = f(a)
    override def filter(p:A => Boolean):Opt[A] = if (p(a)) this else Non
}

object Opt {
  def main(args : Array[String]) : Unit = {
      println(Som(1) flatMap{_:Int => Som(1)})          // Som(1)
      println(Som(1) flatMap{_:Int => Iterable(1, 2)})  // List(1, 2)

      println(for {
           y <- List(1, 2)
           x:Int <- Som(y)
      } yield (x,  y)) // List((1,1), (2,2))
  }
}


2010/8/30 HamsterofDeath <h-star@gmx.de>
do you have an example where what you are trying to so makes sense?

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland