In the code below, there's a DogSearcher which has a method called fetch that expects a Ball. We could also have a CatSearcher with a fetch method that expected a Bell. The idea is that we can call fetch on an instance that inherits from PetSearcher and provide different arguments to it.
Any idea what I'm missing?
trait PetSearcher {
def search(what: Thing): Unit = {
println("default searching")
}
}
class DogSearcher extends PetSearcher {
def search(what: Ball): Unit = {
println("dog searching")
}
}
trait Thing {
val name: String
}
case class Ball(name: String) extends Thing
class Pet {
def fetch[S <: PetSearcher, F <: Thing](searcher: S, what: F): Unit = {
println(what)
searcher.search(what)
}
}
class Dog extends Pet {
val searcher = new DogSearcher()
val ball = new Ball("red")
def go(): Unit = {
fetch[DogSearcher, Ball](searcher, ball)
}
}
//should use DogSearcher but calls fetch on Search, not on DogSearcher.
// So prints default searching, not dog searching..
new Dog().go()
The search
method of DogSearch does not override the search
method of PetSearcher because the argument types are different (what
is a Thing
for PetSearcher but a Ball
for DogSearcher - the fact that Ball
extends Thing
is not enough to make the function calls the same).
Let PetSearcher
take a type parameter to define the type of what
, then DogSearcher
can override this (note the override
keyword becomes necessary):
trait PetSearcher[T] {
def search(what: T): Unit = {
println("default searching")
}
}
class DogSearcher extends PetSearcher[Ball] {
override def search(what: Ball): Unit = {
println("dog searching")
}
}
To get it to compile, you also need to update the use of PetSearcher in Pet, by adding the type parameter F (the subtype of Thing that this Pet searches for) to the PetSearcher:
def fetch[S <: PetSearcher[F], F <: Thing](searcher: S, what: F): Unit = {