Type signature changes when overriding method

428 views
Skip to first unread message

Kaylen Wheeler

unread,
May 18, 2015, 2:01:34 AM5/18/15
to scala...@googlegroups.com
This is best explained with the code and error message below:


class first{
def classes:List[Class[_ <: Any]] = List()
}
class second extends first{
override def classes = List(classOf[Int])
}
class third extends second{
override def classes = List(classOf[String])
}


error: type mismatch;
 found   : Class[String](classOf[java.lang.String])
 required: Class[Int]
        override def classes = List(classOf[String])


Does anyone know why the type signature of "classes" changes after inheritance?

Jason Zaugg

unread,
May 18, 2015, 2:36:31 AM5/18/15
to Kaylen Wheeler, scala-user

Here’s a simpler program to show what’s happening.

trait first {
  def m: Any
}
class second extends first {
  override def m = 1
}
class third extends second {
  override def m = ""
}

It is legal for an overriding method in a subclass to tighten the return type. This is known as a  "covariant return type".

second#classes takes the inferred type for m of => Int which is legal. The body of third#m has the type String, but it would be illegal to use this as the return type as it isn't a subtype of Int

% scalac -Xprint:typer sandbox/test.scala 2>&1 | egrep 'class|classes'
  override def classes = ""
    def m: Any
  class second extends AnyRef with first {
    override def m: Int = 1
  class third extends second {
    override def <m: error>: <error> = ""

If we put the inferred types in explicitly, we get:

sandbox/test.scala:8: error: overriding method m in class second of type => Int;
 method m has incompatible type
  override def m: String = ""

Why did’t the compiler issue that in your example? Instead, it tried to directly inherit the return type of String from the overriden method second#m, and see if it could adapt the body of third#m, perhaps with an implicit conversion. For example, this is allowed

% cat sandbox/test.scala && (scalac -Xprint:typer sandbox/test.scala 2>&1 )
trait first {
  def m: Any
}
class second extends first {
  override def m = 1
}
class third extends second {
  implicit def s2i(s: String) = 0
  override def m = ""
}
[[syntax trees at end of                     typer]] // test.scala
package <empty> {
  abstract trait first extends scala.AnyRef {
    def m: Any
  };
  class second extends AnyRef with first {
    def <init>(): second = {
      second.super.<init>();
      ()
    };
    override def m: Int = 1
  };
  class third extends second {
    def <init>(): third = {
      third.super.<init>();
      ()
    };
    implicit def s2i(s: String): Int = 0;
    override def m: Int = third.this.s2i("")
  }
}

-jason

Som Snytt

unread,
May 18, 2015, 3:21:55 AM5/18/15
to Jason Zaugg, Kaylen Wheeler, scala-user
Someone asked this again the other day; it's surprisingly frequent and surprisingly confusing.

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages