Jay Taylor's notes

back to listing index

scala - define method to return type of class extending it - Stack Overflow

[web search]
Original source (stackoverflow.com)
Tags: stackoverflow.com
Clipped on: 2012-08-09

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
asked Mar 16 '11 at 21:02
Image (Asset 1/3) alt=3,068733

87% accept rate
add comment

3 Answers

up vote 2 down vote accepted

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
}
answered Mar 16 '11 at 21:59
Image (Asset 2/3) alt=7,48621531
  upvote
 flag
  upvote
 flag
This works better, but is there any way to get around the issue shown in the second edit? – luxun Mar 16 '11 at 23:23
add comment

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.

answered Mar 16 '11 at 22:09
Image (Asset 3/3) alt=9,3921838
  upvote
 flag
This doesn't work for every situation, although it's better than nothing. See edit in post for more details. – luxun Mar 16 '11 at 22:39
  upvote
 flag
@aharon: Yeah, this.type is kind of singleton type. You can reed more about it here. So it's not for all possible scenarios... – tenshi Mar 16 '11 at 22:47
add comment

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

answered Mar 16 '11 at 22:59
tenshi
9,3921838
  upvote
 flag
This works better, but is there any way to get around the issue shown in the second edit? – luxun Mar 16 '11 at 23:22
add comment

Your Answer

 
community wiki

Not the answer you're looking for? Browse other questions tagged or ask your own question.