Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

define method to return type of class extending it

Tags:

types

scala

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
like image 486
Aaron Yodaiken Avatar asked Mar 16 '11 21:03

Aaron Yodaiken


2 Answers

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
}
like image 199
Rafał Rawicki Avatar answered Oct 17 '22 02:10

Rafał Rawicki


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.

like image 7
tenshi Avatar answered Oct 17 '22 00:10

tenshi