Reputation: 3814
Let's say I have the following trait
trait Foo[T] {
def overrideMe(other:Foo[T]) : Int
}
I would like to be able to do
class Bar extends Foo[Int] {
override def overrideMe(other:Bar) : Int = other.BarFn
}
but it does not compile. The reason is that I would like overrideMe to be able to use functionality of the subtype. I could do something like
class Bar extends Foo[Int] {
override def overrideMe(other:Foo[Int]) : Int = {
other.asInstanceOf[Bar].BarFn
}
but that doesn't look very nice.
Is it possible to say in the trait that the virtual funcation can be overridden with a subtype?
edit @agilesteel That almost works, but I run into trouble if I have a function in another class relying only on the trait Foo
class Test[T] {
def callOverrideMe(a : Foo[T], b : Foo[T] ) : Int = a.overrideMe(b)
}
I get a compile error: type mismatch; found b.type (with underlying type foo.Foo[T]) required a.SubType
Upvotes: 5
Views: 1586
Reputation: 21112
For the second part of your question, agilesteel's solution can work with a type refinement,
trait Foo[T] {
type R <: Foo[T]
def overrideMe(other: R) : Int
}
class Bar extends Foo[Int] {
type R = Bar
override def overrideMe(other: Bar) : Int = other.barFn
def barFn = 10
}
def callOverrideMe[T, R0 <: Foo[T]{type R = R0}](a : R0, b : R0) : Int = {
a.overrideMe(b)
}
There might be difficulty inferring type T
in callOverrideMe
. A way to improve type inference is to make R
higher kinded---i.e., give it its own type parameter T
.
This solution is basically equivalent to Alexey Romanov's, and demonstrates how abstract types can be surfaced as type parameters.
Upvotes: 4
Reputation: 170899
class Test[T] {
def callOverrideMe(a : Foo[T], b : Foo[T] ) : Int = a.overrideMe(b)
}
Of course, you can't get it to work with this signature. Just consider
class Baz extends Foo[Int] {...}
new Test[Int].callOverrideMe(new Bar, new Baz)
This should be the same as new Bar.overrideMe(new Baz)
, but you don't want it to compile!
You can use the curiously recurring template pattern for this:
trait Foo[T, Sub <: Foo[T, Sub]] {
def overrideMe(other:Sub) : Int
}
class Bar extends Foo[Int, Bar] {
override def overrideMe(other:Bar) : Int = other.BarFn
}
class Test[T] {
def callOverrideMe[Sub <: Foo[T, Sub]](a : Sub, b : Sub) : Int = a.overrideMe(b)
}
What I am trying to accomplish is an algorithm relying on a trait and then have some functions implemented in the sub types. Are there any other good design pattern for this?
Check out Scalaz type classes. E.g. https://github.com/scalaz/scalaz/blob/master/core/src/main/scala/scalaz/Equal.scala
Upvotes: 6
Reputation: 16859
trait Foo[T] {
type TheSubType <: Foo[T]
def overrideMe(other: TheSubType) : Int
}
class Bar extends Foo[Int] {
type TheSubType = Bar
override def overrideMe(other: Bar) : Int = other.barFn
def barFn = 10
}
Upvotes: 7