Scala Case Classes

March 31, 2020

If you’ve done Python before, a case class in Scala is similar to collections.namedtuple and data classes (available only in Python 3.7+). These are all tools used commonly to model immutable structured data.

It’s a thin abstraction over a normal class, with a constructor used to assign values to named fields. Here is a simple example of a case class in Scala.

scala> case class Person(name: String, email: String, age: Int)
defined class Person

scala> val bob = Person("Bob", "bob@me.com", 24)
bob: Person = Person(Bob,bob@me.com,24)

scala> Person(age = 24, name = "Bob", email = "bob@me.com")
res0: Person = Person(Bob,bob@me.com,24)

The same class expressed as a namedtuple:

from collections import namedtuple
Person = namedtuple("Person", ["name", "email", "age"])
bob = Person("bob", "bob@me.com", 24)

or as a data class (note the frozen=True parameter to more closely mimic the immutability aspect of the other two):

from dataclasses import dataclass

@dataclass(frozen=True)
class Person:
    name: str
    email: str
    age: int
    
bob = Person("bob", "bob@me.com", 24)

Pattern matching

The immutability aspect of case classes makes it possible to do certain operations more naturally than just plain Scala classes.

Equality

Comparisons between instances of case classes are done by value, not by reference.

scala> val bob = Person("Bob", "bob@me.com", 24)
bob: Person = Person(Bob,bob@me.com,24)

scala> val bob2 = Person("Bob", "bob@me.com", 24)
bob2: Person = Person(Bob,bob@me.com,24)

scala> bob == bob2
res1: Boolean = true

Pattern matching

In particular, case classes support pattern matching:

scala> abstract class Animal
defined class Animal

scala> case class Person(name: String, email: String, age: Int) extends Animal
defined class Person

scala> case class Dog(name: String, age: Int) extends Animal
defined class Dog

scala> object Census {
     |   def check(a: Animal): String = a match {
     |     case Person("bob", _, _) => "Hey, it's Bob!"
     |     case Person(_, _, _) => "Hey, it's a person"
     |     case Dog(_, _) => "Woof!"
     |   }
     | }
defined object Census

scala> val bob = Person("Bob", "bob@me.com", 24)
bob: Person = Person(Bob,bob@me.com,24)

scala> val andy = Person("Andy", "andy@me.com", 26)
andy: Person = Person(Andy,andy@me.com,26)

scala> val fluffball = Dog("fluffball", 2)
fluffball: Dog = Dog(fluffball,2)

scala> Census.check(bob)
res0: String = Hey, it's Bob!

scala> Census.check(andy)
res1: String = Hey, it's a person

scala> Test.check(fluffball)
res2: String = Woof!

This page will be updated with more information relevant to case classes as I delve deeper into the world of Scala!

More readings:

Case Classes | Scala Book | Scala Documentation

A Quick Review of Scala’s Case Classes