Search code examples
scalatypestype-inferencedynamic-dispatchmultiple-dispatch

Scala dynamic dispatch with parameterized function


How is it possible to make this code work?

As far as I know, Scala doesn't have dynamic dispatch (similar to Java). Is it possible to simulate dynamic dispatching somehow?

Or what's the best solution?

object Tezt {

  case class SuperClazz()
  case class SubClazz1() extends SuperClazz
  case class SubClazz2() extends SuperClazz

  def method(obj: SubClazz1) = {
    // stuff
  }

  def method(obj: SubClazz2) = {
    // stuff
  }

  def func[T <: SuperClazz](obj: T) = {
    Tezt.method(obj) // Error: Cannot resolve method reference with such signature
  }
}

Solution

  • The standard way to implement dynamic dispatch on a single argument is object-oriented polymorphism:

    abstract class SuperClazz() {
      def method(): ReturnType
    }
    case class SubClazz1() extends SuperClazz {
      def method() = {
        // stuff
      }
    }
    case class SubClazz2() extends SuperClazz {
      def method() = {
        // stuff
      }
    }
    
    // Alternatively just `def func(obj: SuperClazz) =` in this case
    def func[T <: SuperClazz](obj: T) = 
      obj.method()
    

    Mind that you can't extend a case class with another case class, and it's usually considered bad style to extend case classes at all. To implement this you'd probably need method to be abstract in SuperClazz, and thus SuperClazz should be a trait or an abstract class.

    Another common alternative for dynamic dispatch in scala is a pattern match:

    sealed abstract class SuperClazz()
    case class SubClazz1() extends SuperClazz
    case class SubClazz2() extends SuperClazz
    
    def method(obj: SubClazz1) = {
      // stuff
    }
    
    def method(obj: SubClazz2) = {
      // stuff
    }
    
    def func(obj: SuperClazz) =
      obj match {
        case sc1: SubClazz1 => method(sc1)
        case sc2: SubClazz2 => method(sc2)
      }
    

    It's common to implement pattern matches like this when the super class or trait is sealed (in this case sealed abstract class SuperClazz()). When matching on objects of sealed super classes, the compiler will check that you have listed every possibility in the match, to ensure there won't be a runtime error when matching. The compiler will give you a warning if you forget to specify some possibilities.

    Pattern matches also work for multiple argument dynamic dispatch, but compared to polymorphism, they usually require writing more boilerplate code and may have a greater runtime performance cost for linearly testing every match case and calling unapply functions.