Case Objects

Before we jump into case objects, I should provide a little background on regular Scala objects. As I mentioned early on in this book, you use a Scala object when you want to create a singleton object. As the official Scala documentation states, “Methods and values that aren’t associated with individual instances of a class belong in singleton objects, denoted by using the keyword object instead of class.”

A common example of this is when I create a “utilities” object, such as this one:

object PizzaUtils {
    def addTopping(p: Pizza, t: Topping): Pizza = ...
    def removeTopping(p: Pizza, t: Topping): Pizza = ...
    def removeAllToppings(p: Pizza): Pizza = ...
}

Or this one:

object FileUtils {
    def readTextFileAsString(filename: String): Try[String] = ...
    def copyFile(srcFile: File, destFile: File): Try[Boolean] = ...
    def readFileToByteArray(file: File): Try[Array[Byte]] = ...
    def readFileToString(file: File): Try[String] = ...
    def readFileToString(file: File, encoding: String): Try[String] = ...
    def readLines(file: File, encoding: String): Try[List[String]] = ...
}

This is the most common way I use the Scala object construct.

Case objects

A case object is like an object, but just like a case class has more features than a regular class, a case object has more features than a regular object. It specifically has two important features that make it useful:

  • It is serializable
  • It has a default hashCode implementation

These features make a case object useful when you don’t know how it will be used by other developers, such as if it will be sent across a network, or even referenced in a different JVM (such as with the Akka actors platform, where you can send messages between JVM instances).

Because of these features, I primarily use case objects (instead of regular objects) in two places:

  • When creating enumerations
  • When creating containers for “messages” that I want to pass between other objects (such as with Akka)

Creating enumerations with case objects

As I showed earlier in this book, you create enumerations in Scala like this:

sealed trait Topping
case object Cheese extends Topping
case object Pepperoni extends Topping
case object Sausage extends Topping
case object Mushrooms extends Topping
case object Onions extends Topping

sealed trait CrustSize
case object SmallCrustSize extends CrustSize
case object MediumCrustSize extends CrustSize
case object LargeCrustSize extends CrustSize

sealed trait CrustType
case object RegularCrustType extends CrustType
case object ThinCrustType extends CrustType
case object ThickCrustType extends CrustType

Then later in your code you use those enumerations like this:

case class Pizza (
    crustSize: CrustSize,
    crustType: CrustType,
    toppings: Seq[Topping]
)

Using case objects as messages

Another place where case objects come in handy is when you want to model the concept of a “message.” For example, imagine that you’re writing an application like Amazon’s Alexa, and you want to be able to pass around “speak” messages like, “speak the enclosed text,” “stop speaking,”, “pause,” and “resume.” In Scala you create singleton objects for those messages like this:

case class StartSpeakingMessage(textToSpeak: String)
case object StopSpeakingMessage
case object PauseSpeakingMessage
case object ResumeSpeakingMessage

Notice that StartSpeakingMessage is defined as a case class rather than a case object. This is because an object can’t have any constructor parameters.

Given those messages, if Alexa was written using the Akka library, you’d find code like this in a “speak” class:

class Speak extends Actor {
  def receive = {
    case StartSpeakingMessage(textToSpeak) =>
        // code to speak the text
    case StopSpeakingMessage =>
        // code to stop speaking
    case PauseSpeakingMessage =>
        // code to pause speaking
    case ResumeSpeakingMessage =>
        // code to resume speaking
  }
}

This is a good, safe way to pass messages around in a Scala application.

See also

I won’t cover the Akka actor library in this book, but I’ve already written several tutorials that provide an introduction to it, several of which demonstrate the use of case objects for messages:

results matching ""

    No results matching ""