packages

Abstract Traits

Basic information on Scala traits can be easily google’d. A concept I ran across that took me a while to fully absorb is the idea of a trait with only abstract methods that can be later implemented in traits that extend the abstract trait.

The above can be mouthful so I will now follow up with a example and explanation.

Abstract Method

The following will serve as the basic foundation of the work we are about to do. A simple base trait that has a abstract method in it.

  trait BaseSpeaker { def speak(words: String) }

Now we will extend BaseSpeaker and actually provide an implementation for speak:

trait SpeakWithTime extends BaseSpeaker {
  abstract override def speak(words: String) = {
    super.speak(s"at [${java.time.Instant.now()}]> $words")
  }
}

Note the abstract override keywords before the method. Without these keywords the compiler will still halt and complain. As far as scala goes this method is still not concrete, in other words we need to mixin a concrete implementation. This requirement is satisfied by using the abstract keyword. The override keyword plays a role in the modification of the concrete implementation that will be provided later down the line. Without both keywords this mechanism fails.

Now we create a Base class that will provide a concrete implementation for speak method by extending BaseSpeaker. This now sets up the stage needed for the rest of the pattern to work as stackable.

class SimpleSpeaker extends BaseSpeaker {
  override def speak(words: String) {
    p("[speaker begins] " + words)
  }
}

In order to tap into the SpeakWithTime’s modified speak method we need to create a class that will mixin the new trait on top the concrete implementation of the speak method. In this case that will be satisfied by extending SimpleSpeaker. As you may correctly guess, order does matter here.

  class ComplexSpeaker extends SimpleSpeaker with SpeakWithTime {}

All of the code together for cut-and-paste purposes appears below:

package AbstractTraitExample {

  trait BaseSpeaker { def speak(words: String) }

  trait SpeakWithTime extends BaseSpeaker {
    abstract override def speak(words: String) = {
      super.speak(s"at [${java.time.Instant.now()}]> $words")
    }
  }

  class SimpleSpeaker extends BaseSpeaker {
    override def speak(words: String) {
      p("[speaker begins] " + words)
    }
  }

  class ComplexSpeaker extends SimpleSpeaker with SpeakWithTime {}
}

import AbstractTraitExample._
object Examples extends App {
  val speaker = new ComplexSpeaker
  speaker.speak("Hiya: ")
}