scalaoopgenericstypesscala-generics

Scala - call method on upper bounded instance


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() 

Solution

  • 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 = {