Scala: how to refer to the type of an extending class in a superclass?

scala, types

Solution

The standard solution to this problem is F-bounded polymorphism (which isn't Scala-specific—you'll find it used in Java, etc.):

trait Parent[T <: Parent[T]] {
  def method(t: T)
}

class Child1 extends Parent[Child1] {
  def method(t: Child1) = println("child1's method")
}

class Child2 extends Parent[Child2] {
  def method(t: Child2) = println("child1's method")
}

As a side note, there's been some grumbling about F-bounded polymorphism in the Scala community—Kris Nuttycombe for example says it's "tricky to get right and causes typing clutter in the codebase", and I've personally found that I use it less and less often after several years of writing Scala. When your program architecture leads you to need this kind of inheritance, though, it's exactly the right tool for the job.

The problem with `this.type` (mentioned in the comments) is that it won't allow you to do most of the things you'd reasonably want to do—it's too specific:

scala> abstract class Parent {
     |   def method(t: this.type)
     | }
defined class Parent


scala> class Child1 extends Parent {
     |   def method(t: this.type) = println("child1's method")
     | }
defined class Child1

scala> val a = new Child1
a: Child1 = Child1@19517e9a

scala> val b = new Child1
b: Child1 = Child1@5737e545

scala> a.method(b)
<console>:12: error: type mismatch;
 found   : b.type (with underlying type Child1)
 required: a.type
              a.method(b)
                       ^

The only argument we can pass to `a.method` is `a` itself.

Problem

Is there a way to define a type `T` in Parent such that `T` will always become the actual type of the extending class (in this case `Child`)? In `Parent`, I want to enforce/declare `T` to be always the extending type, as if I would have written `type T="type_of_the_extending_class"` in each actual extending class without actually writing down the lines `type T=Child1` in `Child1` etc. So Child1's method only should accept Child1 objects as parameter and Child2's method should only accept Child2 objects. Is there a simpler way to enforce this? Is there a way without writing `type T=ChildX` in each `ChildX` class ? Is there a way without this boilerplate? I've been searching for a solution in Scala books but did not find any. ``` abstract class Parent{ type T<:Parent def method(t:T) } class Child1 extends Parent{ type T=Child1 override def method(t:T)=t.child1method def child1method=println("child1's method") } class Child2 extends Parent{ type T=Child2 override def method(t:T)=t.child2method def child2method=println("child2's method") } ```

Original source