Aaron Yodaiken
Aaron Yodaiken

Reputation: 19551

define method to return type of class extending it

I'd like to be able to do something like this:

trait A {
  def f(): ???_THE_EXTENDING CLASS
}
class C extends A {
  def f() = self
}
class D extends A {
  def f() = new D
}
class Z extends D {
  def f() = new Z
}

And the following would not compile, given the above code

class Bad1 extends A {
  def f() = "unrelated string"
}
class Bad2 extends A {
  def f() = new C // this means that you can't just define a type parameter on
                  // A like A[T <: A] with f() defined as f: T
}
class Bad3 extends D // f() now doesn't return the correct type

Is there a name for this kind of relationship? And how is it annotated/implemented in Scala?

Edit

The following sort of works, as you can see:

scala> trait A {
     | def f: this.type 
     | }
defined trait A

scala> class C extends A {
     | def f = this 
     | }
defined class C

scala> class D extends A {
     | def f = new D
     | }
<console>:7: error: type mismatch;
 found   : D
 required: D.this.type
       def f = new D
               ^

Is there a way to get around that?

Edit 2

Using the second system, I can do this, which is good up to the definition of class D:

scala> trait A[T <: A[T]] { def f(): T }
defined trait A
// OR
scala> trait A[T <: A[T]] { self: T =>
     | def f(): T
     | }

scala> class C extends A[C] { def f() = new C }
defined class C

scala> class D extends C
defined class D

scala> (new D).f
res0: C = C@465fadce

Upvotes: 24

Views: 9965

Answers (3)

Rafał Rawicki
Rafał Rawicki

Reputation: 22690

I'm afraid there is no possibility to know what is the extended class from the extending class.

The closest to what you'd like to have is something similar to Curiously Recurring Template Pattern (CRTP) well known from C++.

trait A[T <: A[T]] {
  def f(): T;
}

class C extends A[C] {
  def f() = new C
}

class D extends A[D] {
  def f() = new D
}

Upvotes: 15

tenshi
tenshi

Reputation: 26566

Here is another possible solution. It's combination of self type + type parameter:

trait A[T <: A[T]] { self: T =>
  def f(): T
}

class Z extends A[Z] {
  override def f() = new Z
  def x = "x"
}

println((new Z).f().x)

Here you can find more info about this solution:

scala self-type: value is not a member error

Upvotes: 5

tenshi
tenshi

Reputation: 26566

One thing that you can do, is to return type this.type:

trait A {
  def f(): this.type
}

class C extends A {
  def f() = this
}

class D extends A {
  def f() = this
}

class Z extends D {
  override def f() = this
  def x = "x"
}

println((new Z).f().x)

This can be useful for builders.

Upvotes: 7

Related Questions