Improve Scala23 mei 2014

What is a self type in Scala?

In Scala you do not need any fancy framework to do dependecy injection. Scala provides you with the option to mixin traits. When you search the internet how to do dependency injection in Scala you will find terms like traits, mixin, self types and cake pattern. In this blog I will try to clarify how to do dependency injection in Scala.

What is a trait

A trait is like a Java interface, but it can contain implementations of functions. So a trait can be used just like in Java to specify that a class conforms to a specific interface and thus is of a specific type. For instance when the class Car extends the trait Vehicle then Car‘is a‘ Vehicle. Which makes perfect sense.

trait Vehicle
class Car extends Vehicle

Now let’s assume that we want to plug in a horn to the car. This can be easily done by mixin a trait Horn

trait Horn {
  def toot(): Unit = println("Toot!")
}
class Car extends Vehicle with Horn

Now calling new Car toot() will print Toot!. While it was easy to code, the result is not correct. This now means that the Car‘is a‘ Horn and that certainly is not correct. The other problem is that the Car is tied to this specific Horn. So the Car is constructed, but the Horn is not injected into the Car, but rather implemented with the Car. So this is not dependency injection and the type is wrong.

Constructor Parameter

The easy solution would be to pass the Horn to the Car as a class parameter. This would express that the Car‘has a‘ Horn. This is exactly what we want, so let’s do that.

trait Horn {
  def toot(): Unit
}
class LoudHorn extends Horn {
  override def toot(): Unit = println("TOOT!")
}
class SoftHorn extends Horn {
  override def toot(): Unit = println("toot")
}
class Car(horn: Horn) extends Vehicle {
  def toot(): Unit = horn.toot()
}

Now we can call new Car(new LoudHorn) toot() which will sound a loud horn and new Car(new SoftHorn) toot() to blow the soft horn. There is some more boilerplate in here to expose the horn to the outside world. The LoudHorn and SoftHorn can no longer be traits and need to be classes.

trait Horn {
  def toot(): Unit
}
trait LoudHorn extends Horn {
  override def toot(): Unit = println("TOOT!")
}
trait SoftHorn extends Horn {
  override def toot(): Unit = println("toot")
}
class Car extends Vehicle { this: Horn =>
}

Now calling new Car with LoudHorn toot() has the same result as in the previous snippet, but the LoudHorn and SoftHorn are no longer classes. This means that they have no constructor and therefore cannot exist on their own. This is sometimes a much nicer pattern, but this can be argued. The difference, according to me, is that with the self type you add some functionality to a class, whereas with using with constructor arguments you create a Car that has a horn that can be blown.