Reputation: 11
abstract class TileStack {
def foo = 1 + 1
def extendRight: HashSet[_ <: TileStack]
}
class SquareTileStack extends TileStack {
def extendRight = {
def rec(currentStack: SquareTileStack = new SquareTileStack) = {
// if(currentStack.isDoneRecursing)
// else rec(currentStack)
}
rec()
}
}
class TriangleTileStack extends TileStack {
def extendRight = {
def rec(currentStack: TriangleTileStack = new TriangleTileStack) = {
// if(currentStack.isDoneRecursing)
// else rec(currentStack)
}
rec()
}
}
object Tiler {
def bar[T <: TileStack](stackList: HashSet[T]) = {
var stackDictionary: HashSet[(T, HashSet[T])] = HashSet()
for(stack: [T] <- stackList){
hashOfHash += ((stack, stack.extendRight))
}
}
}
There are two problems:
1) This code won't compile because of the method Tiler.bar
. This is because the stackDictionary
is expecting a value of type HashSet[T]
where T <: TileStack
but is instead getting some random value _ <: TileStack
. Realistically, T is actually being returned by extendRight
, but this is not specified in the abstract class. What I would like to do is something like this:
abstract class TileStack {
def foo = 1 + 1
def extendRight: HashSet[this]
}
But that is obviously not allowed. What is the solution?
2) Notice that the actual implementations of extendRight are ugly. This is because extendRight is intended to be a recursive method that takes a default parameter of the current type, and builds it up as an extension of this
. However, I could not figure out how to take a default parameter for an interface method without running into type issues.
Using the suggestion from som-snytt, this is the solution I came up with. I find it an incredibly odd construction, to use type parameters in this way, but it is what it is.
abstract class TileStack[Repr <: TileStack[Repr]] {
def foo = 1 + 1
def make(): TileStack[Repr]
def extendRight(currentStack: TileStack[Repr] = make): HashSet[Repr]
}
class SquareTileStack extends TileStack[SquareTileStack] {
def make = new SquareTileStack
def extendRight(currentStack: TileStack[SquareTileStack] = make) = {
new HashSet[SquareTileStack]
}
}
class TriangleTileStack extends TileStack[TriangleTileStack] {
def make = new TriangleTileStack
def extendRight(currentStack: TileStack[TriangleTileStack] = make) = {
new HashSet[TriangleTileStack]
}
}
class Tiler {
def bar[Repr <: TileStack[Repr]](stackList: HashSet[Repr]) = {
var stackDictionary: HashSet[(Repr, HashSet[Repr])] = HashSet()
for (stack <- stackList) {
stackDictionary += ((stack, stack.extendRight()))
}
}
}
Upvotes: 1
Views: 102
Reputation: 7979
You're looking for the MyType concept, which is not directly modeled in Scala. The most common way to encode it is with F-bounded polymorphism, which is also used under the covers in Java enums:
public abstract class Enum<E extends Enum<E>> {
public final int compareTo(E o) { ... }
public final Class<E> getDeclaringClass() { ... }
}
In Scala, you can also use bounded abstract type members, as Martin pointed out in the linked thread:
abstract class C {
type MyType <: C
def foo: MyType
}
case class D(x: Int) extends C {
type MyType = D
def foo = D(123)
}
Upvotes: 0
Reputation: 26486
In any type definition (class or trait) the type is available as this.type
, but that is a path-dependent type and every instance of the type has a distinct this.type
. The most common use of this.type
is to signal to client code that a method returns the same instance on which it was invoked.
Upvotes: 2