Chapter 4. Traits

Introducing Traits

Before we dive into object-oriented programming, there’s one more essential feature of Scala that you should get acquainted with: traits. Understanding the value of this feature requires a little backstory.

In Java, a class can implement an arbitrary number of interfaces. This model is very useful for declaring that a class exposes multiple abstractions. Unfortunately, it has one major drawback.

For many interfaces, much of the functionality can be implemented with boilerplate code that will be valid for all classes that use the interface. Java provides no built-in mechanism for defining and using such reusable code. Instead, Java programmers must use ad hoc conventions to reuse implementation code for a given interface. In the worst case, the developer just copies and pastes the same code into every class that needs it.

Often, the implementation of an interface has members that are unrelated (“orthogonal”) to the rest of the instance’s members. The term mixin is often used for such focused and potentially reusable parts of an instance that could be independently maintained.

Have a look at the following code for a button in a graphical user interface, which uses callbacks for “clicks”:

// code-examples/Traits/ui/button-callbacks.scala

package ui

class ButtonWithCallbacks(val label: String,
    val clickedCallbacks: List[() => Unit]) extends Widget {

  require(clickedCallbacks != null, "Callback list can't be null!")

  def this(label: String, clickedCallback: () => Unit) =
    this(label, List(clickedCallback))

  def this(label: String) = {
    this(label, Nil)
    println("Warning: button has no click callbacks!")
  }

  def click() = {
    // ... logic to give the appearance of clicking a physical button ...
    clickedCallbacks.foreach(f => f())
  }
}

There’s a lot going on here. The primary constructor takes a label argument and a list of callbacks that are invoked when the button’s click method is invoked. We’ll explore this class in greater detail in Chapter 5. For now, we want to focus on one particular problem. Not only does ButtonWithCallbacks handle behaviors essential to buttons (like clicking), it also handles notification of click events by invoking the callback functions. This goes against the Single Responsibility Principle (see [Martin2003]), a means to the design goal of separation of concerns. We would like to separate the button-specific logic from the callback logic, such that each logical component becomes simpler, more modular, and more reusable. The callback logic is a good example of a mixin.

This separation is difficult to do in Java, even if we define an interface for the callback behavior. We still have to embed the implementation code in the class somehow, compromising modularity. The only other alternative is to use a specialized tool like aspect-oriented programming (AOP; see [AOSD]), as implemented by AspectJ (see [AspectJ]), an extension of Java. AOP is primarily designed to separate the implementations of “pervasive” concerns that are repeated throughout an application. It seeks to modularize these concerns, yet enable the fine-grained “mixing” of their behaviors with other concerns, including the core domain logic of the application, either at build or runtime.

Traits As Mixins

Scala provides a complete mixin solution, called traits. In our example, we can define the callback abstraction in a trait, as in a Java interface, but we can also implement the abstraction in the trait (or a derived trait). We can declare classes that “mix in” the trait, much the way you can declare classes that implement an interface in Java. However, in Scala we can even mix in traits at the same time we create instances. That is, we don’t have to declare a class first that mixes in all the traits we want. So, Scala traits preserve separation of concerns while giving us the ability to compose behavior on demand.

If you come from a Java background, you can think of traits as interfaces with optional implementations. Or, if you prefer, you can think of traits as a “constrained” form of multiple inheritance. Other languages provide constructs that are similar to traits, such as modules in Ruby, for example.

Let’s use a trait to separate the callback handling from the button logic. We’ll generalize our approach a little bit. Callbacks are really a special case of the Observer Pattern (see [GOF1995]). So, let’s create a trait that implements this pattern, and then use it to handle callback behavior. To simplify things, we’ll start with a single callback that counts the number of button clicks.

First, let’s define a simple Button class:

// code-examples/Traits/ui/button.scala

package ui

class Button(val label: String) extends Widget {
  def click() = {
    // Logic to give the appearance of clicking a button...
  }
}

Here is the parent class, Widget:

// code-examples/Traits/ui/widget.scala

package ui

abstract class Widget

The logic for managing callbacks (i.e., the clickedCallbacks list) is omitted, as are the two auxiliary constructors. Only the button’s label field and click method remain. The click method now only cares about the visual appearance of a “physical” button being clicked. Button has only one concern, handling the “essence” of being a button.

Here is a trait that implements the logic of the Observer Pattern:

// code-examples/Traits/observer/observer.scala

package observer

trait Subject {
  type Observer = { def receiveUpdate(subject: Any) }

  private var observers = List[Observer]()
  def addObserver(observer:Observer) = observers ::= observer
  def notifyObservers = observers foreach (_.receiveUpdate(this))
}

Except for the trait keyword, Subject looks like a normal class. Subject defines all the members it declares. Traits can declare abstract members, concrete members, or both, just as classes can (see Overriding Members of Classes and Traits for more details). Also like classes, traits can contain nested trait and class definitions, and classes can contain nested trait definitions.

The first line defines a type for an Observer. This is a structural type of the form { def receiveUpdate(subject:Any) }. Structural types specify only the structure a type must support; you could think of them as “anonymous” types.

In this case, the structural type is defined by a method with a particular signature. Any type that has a method with this signature can be used as an observer. We’ll learn more about structural types in Chapter 12. If you’re wondering why we didn’t use Subject as the type of the argument, instead of Any, we’ll revisit that issue in Self-Type Annotations and Abstract Type Members.

The main thing to notice for now is how this structural type minimizes the coupling between the Subject trait and any potential users of the trait.

Note

Subject is still coupled by the name of the method in Observer through the structural type, i.e., to a method named receiveUpdate. There are several ways we can reduce this remaining coupling. We’ll see how in Overriding Abstract Types.

Next, we declare a list of observers. We make it a var, rather than a val, because List is immutable, so we must create a new list when an observer is added using the addObserver method.

We’ll discuss Scala Lists more in The Scala Type Hierarchy and also in Chapter 8. For now, notice that addObserver uses the list cons “operator” method (::) to prepend an observer to the list of observers. The scala compiler is smart enough to turn the following statement:

observers ::= observer

into this statement:

observers = observer :: observers

Note that we wrote observer :: observers, with the existing observers list on the righthand side. Recall that any method that ends with : binds to the right. So, the previous statement is equivalent to the following statement:

observers = observers.::(observer)

The notifyObservers method iterates through the observers, using the foreach method and calls receiveUpdate on each one. (Note that we are using the “infix” operator notation instead of observers.foreach.) We use the placeholder _ to shorten the following expression:

(obs) => obs.receiveUpdate(this)

into this expression:

_.receiveUpdate(this)

This expression is actually the body of an “anonymous function,” called a function literal in Scala. This is similar to a lambda and like constructs used in many other languages. Function literals and the related concept of a closure are discussed in Function Literals and Closures.

In Java, the foreach method would probably take an interface, and you would pass an instance of a class that implements the interface (e.g., the way Comparable is typically used).

In Scala, the List[A].foreach method expects an argument of type (A) => Unit, which is a function taking an instance of type A—where A represents the type of the elements of the list (Observer, in this case)—and returning Unit (like void in Java).

Note

We chose to use a var with immutable Lists for the observers in this example. We could have used a val with a mutable type, like ListBuffer. That choice would make a little more sense for a real application, but we wanted to avoid the distraction of explaining new library classes.

Once again, we learned a lot of Scala from a small example. Now let’s put our Subject trait to use. Here is ObservableButton, which subclasses Button and mixes in Subject:

// code-examples/Traits/ui/observable-button.scala

package ui
import observer._

class ObservableButton(name: String) extends Button(name) with Subject {
  override def click() = {
    super.click()
    notifyObservers
  }
}

We start by importing everything in the observer package, using the _ wildcard. Actually, we have only defined the Subject trait in the package.

The new class uses the with keyword to add the Subject trait to the class. ObservableButton overrides the click method. Using the super keyword (see Overriding Abstract and Concrete Methods), it first invokes the “superclass” method, Button.click, and then it notifies the observers. Since the new click method overrides Button’s concrete implementation, the override keyword is required.

The with keyword is analogous to Java’s implements keyword for interfaces. You can specify as many traits as you want, each with its own with keyword.

A class can extend a trait, and a trait can extend a class. In fact, our Widget class earlier could have been declared to be a trait.

Note

If you declare a class that uses one or more traits and it doesn’t extend another class, you must use the extends keyword for the first trait listed.

If you don’t use extends for the first trait, e.g., you write the following:

// ERROR:
class ObservableButton(name: String) with Button(name) with Subject {...}

You’ll get an error like this:

... error: ';' expected but 'with' found.
       class ObservableButton(name: String) with Button(name) with Subject {...}
                                            ^

The error should really say, “with found, but extends expected.”

To demonstrate this code, let’s start with a class for observing button clicks that simply counts the number of clicks:

// code-examples/Traits/ui/button-count-observer.scala

package ui
import observer._

class ButtonCountObserver {
  var count = 0
  def receiveUpdate(subject: Any) = count += 1
}

Finally, let’s write a test that exercises all these classes. We will use the Specs library (discussed in Specs) to write a Behavior-Driven Development ([BDD]) “specification” that exercises the combined Button and Subject types:

// code-examples/Traits/ui/button-observer-spec.scala

package ui
import org.specs._
import observer._

object ButtonObserverSpec extends Specification {
  "A Button Observer" should {
    "observe button clicks" in {
      val observableButton = new ObservableButton("Okay")
      val buttonObserver = new ButtonCountObserver
      observableButton.addObserver(buttonObserver)

      for (i <- 1 to 3) observableButton.click()
      buttonObserver.count mustEqual 3
    }
  }
}

If you downloaded the code examples from the O’Reilly site, you can follow the directions in its README files for building and running the examples in this chapter. The output of the specs “target” of the build should include the following text:

Specification "ButtonCountObserverSpec"
  A Button Observer should
  + observe button clicks

Total for specification "ButtonCountObserverSpec":
Finished in 0 second, 10 ms
1 example, 1 expectation, 0 failure, 0 error

Notice that the strings A Button Observer should and observe button clicks correspond to strings in the example. The output of a Specs run provides a nice summary of the requirements for the items being tested, assuming good choices were made for the strings.

The body of the test creates an “Okay” ObservableButton and a ButtonCountObserver, which gives the observer to the button. The button is clicked three times, using the for loop. The last line requires the observer’s count to equal 3. If you are accustomed to using an XUnit-style TDD tool, like JUnit (see [JUnit]) or ScalaTest (see [ScalaTestTool] and ScalaTest), then the last line is equivalent to the following JUnit assertion:

assertEquals(3, buttonObserver.count)

Note

The Specs library (see Specs) and the ScalaTest library (see ScalaTest) both support Behavior-Driven Development ([BDD]), a style of Test-Driven Development ([TDD]) that emphasizes the “specification” role of tests.

Suppose we need only one ObservableButton instance? We actually don’t have to declare a class that subclasses Button with Subject. We can incorporate the trait when we create the instance.

The next example shows a revised Specs file that instantiates a Button with Subject mixed in as part of the declaration:

// code-examples/Traits/ui/button-observer-anon-spec.scala

package ui
import org.specs._
import observer._

object ButtonObserverAnonSpec extends Specification {
  "A Button Observer" should {
    "observe button clicks" in {
      val observableButton = new Button("Okay") with Subject {
        override def click() = {
          super.click()
          notifyObservers
        }
      }

      val buttonObserver = new ButtonCountObserver
      observableButton.addObserver(buttonObserver)

      for (i <- 1 to 3) observableButton.click()
      buttonObserver.count mustEqual 3
    }
  }
}

The revised declaration of observableButton actually creates an anonymous class in which we override the click method, as before. The main difference with creating anonymous classes in Java is that we can incorporate traits in this process. Java does not let you implement a new interface while instantiating a class.

Finally, note that the inheritance hierarchy for an instance can be complex if it mixes in traits that extend other traits, etc. We’ll discuss the details of the hierarchy in Linearization of an Object’s Hierarchy.

Stackable Traits

There are a couple of refinements we can do to improve the reusability of our work and to make it easier to use more than one trait at a time, i.e., to “stack” them.

First, let’s introduce a new trait, Clickable, an abstraction for any widget that responds to clicks:

// code-examples/Traits/ui2/clickable.scala

package ui2

trait Clickable {
  def click()
}

Note

We’re starting with a new package, ui2, to make it easier to keep older and newer versions of the examples distinct in the downloadable code.

The Clickable trait looks just like a Java interface; it is completely abstract. It defines a single, abstract method, click. The method is abstract because it has no body. If Clickable were a class, we would have to add the abstract keyword in front of the class keyword. This is not necessary for traits.

Here is the refactored button, which uses the trait:

// code-examples/Traits/ui2/button.scala

package ui2

import ui.Widget

class Button(val label: String) extends Widget with Clickable {
  def click() = {
    // Logic to give the appearance of clicking a button...
  }
}

This code is like Java code that implements a Clickable interface.

When we previously defined ObservableButton (in Traits As Mixins), we overrode Button.click to notify the observers. We had to duplicate that logic in ButtonObserverAnonSpec when we declared observableButton as a Button instance that mixed in the Subject trait directly. Let’s eliminate this duplication.

When we refactor the code this way, we realize that we don’t really care about observing buttons; we care about observing clicks. Here is a trait that focuses solely on observing Clickable:

// code-examples/Traits/ui2/observable-clicks.scala

package ui2
import observer._

trait ObservableClicks extends Clickable with Subject {
  abstract override def click() = {
    super.click()
    notifyObservers
  }
}

The ObservableClicks trait extends Clickable and mixes in Subject. It then overrides the click method with an implementation that looks almost the same as the overridden method shown in Traits As Mixins. The important difference is the abstract keyword.

Look closely at this method. It calls super.click(), but what is super in this case? At this point, it could only appear to be Clickable, which declares but does not define the click method, or it could be Subject, which doesn’t have a click method. So, super can’t be bound, at least not yet.

In fact, super will be bound when this trait is mixed into an instance that defines a concrete click method, such as Button. Therefore, we need an abstract keyword on ObservableClicks.click to tell the compiler (and the reader) that click is not yet fully implemented, even though ObservableClicks.click has a body.

Note

Except for declaring abstract classes, the abstract keyword is only required on a method in a trait when the method has a body, but it calls the super method that doesn’t have a concrete implementation in parents of the trait.

Let’s use this trait with Button and its concrete click method in a Specs test:

// code-examples/Traits/ui2/button-clickable-observer-spec.scala

package ui2
import org.specs._
import observer._
import ui.ButtonCountObserver

object ButtonClickableObserverSpec extends Specification {
  "A Button Observer" should {
    "observe button clicks" in {
      val observableButton = new Button("Okay") with ObservableClicks
      val buttonClickCountObserver = new ButtonCountObserver
      observableButton.addObserver(buttonClickCountObserver)

      for (i <- 1 to 3) observableButton.click()
      buttonClickCountObserver.count mustEqual 3
    }
  }
}

Compare this code to ButtonObserverAnonSpec. We instantiate a Button with the ObservableClicks trait mixed in, but now there is no override of click required. Hence, this client of Button doesn’t have to worry about properly overriding click. The hard work is already done by ObservableClicks. The desired behavior is composed declaratively when needed.

Let’s finish our example by adding a second trait. The JavaBeans specification (see [JavaBeansSpec]) has the idea of “vetoable” events, where listeners for changes to a JavaBean can veto the change. Let’s implement something similar with a trait that vetoes more than a set number of clicks:

// code-examples/Traits/ui2/vetoable-clicks.scala

package ui2
import observer._

trait VetoableClicks extends Clickable {
  val maxAllowed = 1  // default
  private var count = 0

  abstract override def click() = {
    if (count < maxAllowed) {
      count += 1
      super.click()
    }
  }
}

Once again, we override the click method. As before, the override must be declared abstract. The maximum allowed number of clicks defaults to 1. You might wonder what we mean by “defaults” here. Isn’t the field declared to be a val? There is no constructor defined to initialize it to another value. We’ll revisit these questions in Overriding Members of Classes and Traits.

This trait also declares a count variable to keep track of the number of clicks seen. It is declared private, so it is invisible outside the trait (see Visibility Rules). The overridden click method increments count. It only calls the super.click() method if the count is less than or equal to the maxAllowed count.

Here is a Specs object that demonstrates ObservableClicks and VetoableClicks working together. Note that a separate with keyword is required for each trait, as opposed to using one keyword and separating the names with commas, as Java does for implements clauses:

// code-examples/Traits/ui2/button-clickable-observer-vetoable-spec.scala

package ui2
import org.specs._
import observer._
import ui.ButtonCountObserver

object ButtonClickableObserverVetoableSpec extends Specification {
  "A Button Observer with Vetoable Clicks" should {
    "observe only the first button click" in {
      val observableButton =
          new Button("Okay") with ObservableClicks with VetoableClicks
      val buttonClickCountObserver = new ButtonCountObserver
      observableButton.addObserver(buttonClickCountObserver)

      for (i <- 1 to 3) observableButton.click()
      buttonClickCountObserver.count mustEqual 1
    }
  }
}

The expected observer count is 1. The observableButton is declared as follows:

new Button("Okay") with ObservableClicks with VetoableClicks

We can infer that the click override in VetoableClicks is called before the click override in ObservableClicks. Loosely speaking, since our anonymous class doesn’t define click itself, the method lookup proceeds right to left, as declared. It’s actually more complicated than that, as we’ll see later in Linearization of an Object’s Hierarchy.

In the meantime, what happens if we use the traits in the reverse order?

// code-examples/Traits/ui2/button-vetoable-clickable-observer-spec.scala

package ui2
import org.specs._
import observer._
import ui.ButtonCountObserver

object ButtonVetoableClickableObserverSpec extends Specification {
  "A Vetoable Button with Click Observer" should {
    "observe all the button clicks, even when some are vetoed" in {
      val observableButton =
          new Button("Okay") with VetoableClicks with ObservableClicks
      val buttonClickCountObserver = new ButtonCountObserver
      observableButton.addObserver(buttonClickCountObserver)

      for (i <- 1 to 3) observableButton.click()
      buttonClickCountObserver.count mustEqual 3
    }
  }
}

Now the expected observer count is 3. ObservableClicks now has precedence over VetoableClicks, so the count of clicks is incremented, even when some clicks are subsequently vetoed!

So, the order of declaration matters, which is important to remember for preventing unexpected behavior when traits impact each other. Perhaps another lesson to note is that splitting objects into too many fine-grained traits can obscure the order of execution in your code!

Breaking up your application into small, focused traits is a powerful way to create reusable, scalable abstractions and “components.” Complex behaviors can be built up through declarative composition of traits. We will explore this idea in greater detail in Scalable Abstractions.

Constructing Traits

Traits don’t support auxiliary constructors, nor do they accept an argument list for the primary constructor, the body of a trait. Traits can extend classes or other traits. However, they can’t pass arguments to the parent class constructor (even literal values), so traits can only extend classes that have a no-argument primary or auxiliary constructor.

However, like classes, the body of a trait is executed every time an instance is created that uses the trait, as demonstrated by the following script:

// code-examples/Traits/trait-construction-script.scala

trait T1 {
  println( "  in T1: x = " + x )
  val x=1
  println( "  in T1: x = " + x )
}
trait T2 {
  println( "  in T2: y = " + y )
  val y="T2"
  println( "  in T2: y = " + y )
}

class Base12 {
  println( "  in Base12: b = " + b )
  val b="Base12"
  println( "  in Base12: b = " + b )
}
class C12 extends Base12 with T1 with T2 {
  println( "  in C12: c = " + c )
  val c="C12"
  println( "  in C12: c = " + c )
}
println( "Creating C12:" )
new C12
println( "After Creating C12" )

Running this script with the scala command yields the following output:

Creating C12:
  in Base12: b = null
  in Base12: b = Base12
  in T1: x = 0
  in T1: x = 1
  in T2: y = null
  in T2: y = T2
  in C12: c = null
  in C12: c = C12
After Creating C12

Notice the order of invocation of the class and trait constructors. Since the declaration of C12 is extends Base12 with T1 with T2, the order of construction for this simple class hierarchy is left to right, starting with the base class Base12, followed by the traits T1 and T2, and ending with the C12 constructor body. (For constructing arbitrarily complex hierarchies, see Linearization of an Object’s Hierarchy.)

So, while you can’t pass construction parameters to traits, you can initialize fields with default values or leave them abstract. We actually saw this before in our Subject trait, where the Subject.observers field was initialized to an empty list.

If a concrete field in a trait does not have a suitable default value, there is no “fail-safe” way to initialize the value. All the alternative approaches require some ad hoc steps by users of the trait, which is error-prone because they might do it wrong or forget to do it all. Perhaps the field should be left abstract, so that classes or other traits that use this trait are forced to define the value appropriately. We’ll discuss overriding abstract and concrete members in detail in Chapter 6.

Another solution is to move that field to a separate class, where the construction process can guarantee that the correct initialization data is supplied by the user. It might be that the whole trait should actually be a class instead, so you can define a constructor for it that initializes the field.

Class or Trait?

When considering whether a “concept” should be a trait or a class, keep in mind that traits as mixins make the most sense for “adjunct” behavior. If you find that a particular trait is used most often as a parent of other classes, so that the child classes behave as the parent trait, then consider defining the trait as a class instead, to make this logical relationship more clear. (We said behaves as, rather than is a, because the former is the more precise definition of inheritance, based on the Liskov Substitution Principle—see [Martin2003], for example.)

Tip

Avoid concrete fields in traits that can’t be initialized to suitable default values. Use abstract fields instead, or convert the trait to a class with a constructor. Of course, stateless traits don’t have any issues with initialization.

It’s a general principle of good object-oriented design that an instance should always be in a known valid state, starting from the moment the construction process finishes.

Recap and What’s Next

In this chapter, we learned how to use traits to encapsulate and share cross-cutting concerns between classes. We covered when and how to use traits, how to “stack” multiple traits, and the rules for initializing values within traits.

In the next chapter, we explore how the fundamentals of object-oriented programming work in Scala. Even if you’re an old hand at object-oriented programming, you’ll want to read the next several chapters to understand the particulars of Scala’s approach to OOP.

Get Programming Scala now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.