I always call this construct "representation" type, but perhaps that is not the official name (you may hear the term "F-bounded quantification"). Your types Row
and RowBuffer
take a recursive type R
and B
respectively which describes the type of the final implementation ("representation").
In order to make sure you can fill out the methods of the abstract classes, you need to state that the final implementation will conform to R
and B
respectively. You do that using a self-type annotation:
abstract class Row[T <: Table[T, R, B],
R <: Row[T, R, B],
B <: RowBuffer[T, R, B]] extends Immutable {
_: R =>
...
}
abstract class RowBuffer[T <: Table[T, R, B],
R <: Row[T, R, B],
B <: RowBuffer[T, R, B]] extends Mutable {
_: B =>
..
}
Note that you can use any identifier, e.g. this: R =>
or self: R =>
.
With these, it compiles, and you cannot create a non-abstract sub class of these which does not satisfy the constraint.