Update: Clarified and expanded, since the original question w开发者_StackOverflowas simplified too far
I need a pair of traits, each of which refers to the other such that parent and child classes must relate to each other.
trait Parent [C <: Child] {
def foo(c: C)
}
trait Child [P <: Parent] {
def parent: P = ...
def bar = parent.foo(this)
}
Such that implementing classes must come in pairs:
class ActualParent extends Parent [ActualChild] {
def foo(c: ActualChild) = ...
}
class ActualChild extends Child [ActualParent] {
}
Unfortunately, the compiler doesn't like these traits because the generic types aren't complete. Instead of C <: Child
it needs to say C <: Child[
something]
. Leaving them unspecified doesn't work either:
trait Parent [C <: Child[_]] {
def foo(c: C)
}
trait Child [P <: Parent[_]] {
def parent: P = ...
def bar = parent.foo(this)
}
It now complains on the parent.foo(this)
line, because it doesn't know that this
is of the correct type. The type of parent
needs to be Parent[this.type]
for the call to foo
to have the right types.
I'm thinking there must be a way of referring to an object's own type? Or to a type that's required to be itself?
Update: Further to @Daniel's answer, I tried using an abstract type member within the child to state the generic types of the parent type like this:
trait Parent [C <: Child] {
def foo(c: C)
}
trait Child {
type P <: Parent[this.type]
def parent: P = ...
def bar = parent.foo(this)
}
This isn't working when I try to implement it:
class ActualParent extends Parent [ActualChild] {
def foo(c: ActualChild) = ...
}
class ActualChild extends Child {
type P = ActualParent
}
Gives the following error:
overriding type Parent in trait Child with bounds >: Nothing <: Parent[ActualChild.this.type]
type Parent has incompatible type
What does that mean?
You can use the approach given in http://programming-scala.labs.oreilly.com/ch13.html:
abstract class ParentChildPair {
type C <: Child
type P <: Parent
trait Child {self: C =>
def parent: P
}
trait Parent {self: P =>
def child: C
}
}
class ActualParentChildPair1 {
type C = Child1
type P = Parent1
class Child1 extends Child {...}
class Parent1 extends Parent {...}
}
It can be done with abstract type members.
class Parent {
type C <: Child
def child: C = null.asInstanceOf[C]
}
class Child {
type P <: Parent
def parent: P = null.asInstanceOf[P]
}
Further to @Daniel's answer, I can use an abstract type member within the child to state the generic types of the parent type like this:
trait Parent [C <: Child] {
def foo(c: C)
}
trait Child {
type P <: Parent[this.type]
def parent: P = ...
def bar = parent.foo(this)
}
this.type
isn't usable directly in generics, but in a parameter it seems to be fine. This approach is a lot less long-winded than the surrounding abstract class, and allows more flexible uses such as children that are also parents.
You can write C <: Child[_]
.
Even though it wasn't successful, I'll record this avenue as an answer.
Using abstract type members, state bounds for the types referring back to this.type
:
trait Parent {
type C <: Child { type P <: this.type }
def foo(c: C)
}
trait Child {
type P <: Parent { type C <: this.type }
def parent: P
def bar = parent.foo(this)
}
class ActualParent extends Parent {
type C = ActualChild
def foo(c: ActualChild) = println("Hello")
}
class ActualChild extends Child {
type P = ActualParent
def parent = new ActualParent
}
The problem here is that the compiler doesn't connect this
with the child type of the parent, so calling parent.foo(this)
results in:
type mismatch
found : Child.this.type (with underlying type Child)
required: _3.C where val _3: Child.this.P
精彩评论