Eternal1
Eternal1

Reputation: 5625

How do I implement polymorphism in Scala

I've been learning Scala for web-development for quite some time, and I have stumbled upon a lack of interfaces. Coming from PHP, I used interfaces quite a lot for method-level polymorphism and IoC like this:

interface iAnimal
{
    function makeVoice();
}

class Cat implements iAnimal
{
    function makeVoice()
    {
        return "Meow";
    }
}

class Dog implements iAnimal
{
    function makeVoice()
    {
        return "Woof!";
    }
}

class Box
{
    private $_animal;

    function __construct(iAnimal $animal)
    {
        $this->_animal = $animal;         
    }

    function makeSound()
    {
        echo $this->_animal->makeVoice();
    }
}

And so on, and that was an easy way to ensure that anything I passed to the Box object had a makeVoice method that I was calling elsewhere. Now, what I'm curious of, is how do I implement similar functionality with Scala. I tried searching for this, but info is quite scarce. The only answer I found was to use traits, but as far as I know, they are used for concrete implementation, not declaration.

Thanks in advance.

Upvotes: 1

Views: 1861

Answers (2)

Kevin Wright
Kevin Wright

Reputation: 49685

As per other answers, the solution is to use traits :

trait Animal {
  def makeVoice(): Unit //no definition, this is abstract!
}

class Cat extends Animal{
  def makeVoice(): Unit = "Meow"
}
class Dog extends Animal{
  def makeVoice(): Unit = "Woof"
}

class Box(animal:Animal) {
  def makeSound() = animal.makeVoice()
}

A trait in Scala will be directly compiled to an interface in Java. If it contains any concrete members then these will be directly copied into any class that inherits from the trait. You can happily use a Scala trait as an interface from Java, but then you don't get the concrete functionality mixed in for you.


However... this is only part of the picture. What we've implemented so far is subtype polymorphism, Scala also allows for ad-hoc polymorphism (a.k.a typeclasses):

// Note: no common supertype needed here
class Cat { ... }
class Dog { ... }

sealed trait MakesVoice[T] {
  def makeVoice(): Unit
}
object MakesVoice {
  implicit object CatMakesVoice extends MakesVoice[Cat] {
    def makeVoice(): Unit = "Meow"
  }
  implicit object DogMakesVoice extends MakesVoice[Dog] {
    def makeVoice(): Unit = "Woof"
  }
  //helper method, not required, but nice to have
  def makesVoice[T](implicit mv: MakesVoice[T]) = mv
}

import MakesVoice._
//context-bound version
class Box[T : MakesVoice] {
  //using a helper:
  def makeSound() = makesVoice[T].makeVoice()
  //direct:
  def makeSound() = implicitly(MakesVoice[T]).makeVoice()
}

//using an implicit param
class Box[T](implicit mv : MakesVoice[T]) {
  def makeSound() = mv.makeVoice()
}

What's important here is that the MakesVoice type class can be associated with any type regardless of what hierarchies it belongs to. You can even use type classes with primitives or types imported from a 3rd party library that you couldn't possibly retrofit with new interfaces.

Of course, you have parametric polymorphism as well, which you'd probably know better as "generics" :)

Upvotes: 5

Jean
Jean

Reputation: 21595

Traits are used for both declaration and concrete implementation. Here is a direct translation of your example

trait Animal {
  def makeVoice()
}

class Cat extends Animal{
  override def makeVoice(): Unit = "Meow"
}
class Dog extends Animal{
  override def makeVoice(): Unit = "Woof"
}

class Box(animal:Animal) {
  def makeSound()={
    animal.makeVoice()
  }
}

Additionally, you are able to actually define a concrete implementation directly in the trait which is useful for behaviours shared by members of different class hierarchies:

trait Fish{
  def swim()="Swim"
}
class Truit extends Fish 

trait Bird{
  def fly() = "Fly"
}
class Eagle extends Bird

class Duck extends ???{
  def swim=???
  def fly=???
}

The duck both swims ans flies, you could define it as such :

trait Swimmer{
  def swim
}
trait Fish extends Swimmer{
  def swim()="Swim"
}
class Truit extends Fish 
trait Bird{
  def fly()="Fly"
}
class Eagle extends Bird

class Duck extends Bird with Swimmer

Upvotes: 1

Related Questions