Fredrik Jansson
Fredrik Jansson

Reputation: 3814

override function in trait

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

Answers (3)

Kipton Barros
Kipton Barros

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

Alexey Romanov
Alexey Romanov

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

agilesteel
agilesteel

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

Related Questions