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")
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With