You are previewing Programming Scala.

Programming Scala

Cover of Programming Scala by Dean Wampler... Published by O'Reilly Media, Inc.
  1. Programming Scala
    1. SPECIAL OFFER: Upgrade this ebook with O’Reilly
    2. A Note Regarding Supplemental Files
    3. Foreword
    4. Preface
      1. Welcome to Programming Scala
      2. Conventions Used in This Book
      3. Using Code Examples
      4. Safari® Books Online
      5. How to Contact Us
      6. Acknowledgments
    5. 1. Zero to Sixty: Introducing Scala
      1. Why Scala?
      2. Installing Scala
      3. For More Information
      4. A Taste of Scala
      5. A Taste of Concurrency
      6. Recap and What’s Next
    6. 2. Type Less, Do More
      1. In This Chapter
      2. Semicolons
      3. Variable Declarations
      4. Method Declarations
      5. Inferring Type Information
      6. Literals
      7. Tuples
      8. Option, Some, and None: Avoiding nulls
      9. Organizing Code in Files and Namespaces
      10. Importing Types and Their Members
      11. Abstract Types And Parameterized Types
      12. Reserved Words
      13. Recap and What’s Next
    7. 3. Rounding Out the Essentials
      1. Operator? Operator?
      2. Methods Without Parentheses and Dots
      3. Domain-Specific Languages
      4. Scala if Statements
      5. Scala for Comprehensions
      6. Other Looping Constructs
      7. Conditional Operators
      8. Pattern Matching
      9. Enumerations
      10. Recap and What’s Next
    8. 4. Traits
      1. Introducing Traits
      2. Stackable Traits
      3. Constructing Traits
      4. Recap and What’s Next
    9. 5. Basic Object-Oriented Programming in Scala
      1. Class and Object Basics
      2. Parent Classes
      3. Constructors in Scala
      4. Nested Classes
      5. Visibility Rules
      6. Recap and What’s Next
    10. 6. Advanced Object-Oriented Programming In Scala
      1. Overriding Members of Classes and Traits
      2. Companion Objects
      3. Case Classes
      4. Equality of Objects
      5. Recap and What’s Next
    11. 7. The Scala Object System
      1. The Predef Object
      2. Classes and Objects: Where Are the Statics?
      3. Sealed Class Hierarchies
      4. The Scala Type Hierarchy
      5. Linearization of an Object’s Hierarchy
      6. Recap and What’s Next
    12. 8. Functional Programming in Scala
      1. What Is Functional Programming?
      2. Functional Programming in Scala
      3. Recursion
      4. Tail Calls and Tail-Call Optimization
      5. Functional Data Structures
      6. Traversing, Mapping, Filtering, Folding, and Reducing
      7. Pattern Matching
      8. Partial Functions
      9. Currying
      10. Implicits
      11. Implicit Function Parameters
      12. Call by Name, Call by Value
      13. Lazy Vals
      14. Recap: Functional Component Abstractions
    13. 9. Robust, Scalable Concurrency with Actors
      1. The Problems of Shared, Synchronized State
      2. Actors
      3. Actors in Scala
      4. Traditional Concurrency in Scala: Threading and Events
      5. Recap and What’s Next
    14. 10. Herding XML in Scala
      1. Reading XML
      2. Writing XML
      3. Recap and What’s Next
    15. 11. Domain-Specific Languages in Scala
      1. Internal DSLs
      2. External DSLs with Parser Combinators
      3. Recap and What’s Next
    16. 12. The Scala Type System
      1. Reflecting on Types
      2. Understanding Parameterized Types
      3. Variance Under Inheritance
      4. Type Bounds
      5. Nothing and Null
      6. Understanding Abstract Types
      7. Path-Dependent Types
      8. Value Types
      9. Self-Type Annotations
      10. Structural Types
      11. Existential Types
      12. Infinite Data Structures and Laziness
      13. Recap and What’s Next
    17. 13. Application Design
      1. Annotations
      2. Enumerations Versus Pattern Matching
      3. Thoughts On Annotations and Enumerations
      4. Using Nulls Versus Options
      5. Exceptions and the Alternatives
      6. Scalable Abstractions
      7. Effective Design of Traits
      8. Design Patterns
      9. Better Design with Design By Contract
      10. Recap and What’s Next
    18. 14. Scala Tools, Libraries, and IDE Support
      1. Command-Line Tools
      2. Build Tools
      3. Integration with IDEs
      4. Test-Driven Development in Scala
      5. Other Notable Scala Libraries and Tools
      6. Java Interoperability
      7. Java Library Interoperability
      8. Recap and What’s Next
    19. A. References
    20. Glossary
    21. Index
    22. About the Authors
    23. Colophon
    24. SPECIAL OFFER: Upgrade this ebook with O’Reilly
O'Reilly logo

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.

The best content for your career. Discover unlimited learning on demand for around $1/day.