Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Case Class

Case classes in Scala are a special type of class that is optimized for use in pattern matching and immutability by default. They come with several boilerplate features out of the box, such as sensible toString, equals, and hashCode implementations, as well as the ability to be deconstructed in pattern matching. Case classes are immensely useful for defining simple data-holding objects, making them a staple in functional programming and domain modeling in Scala.

Basic Case Class

Defining a case class is straightforward:

case class Person(name: String, age: Int)

This declaration automatically provides:

  • Immutable fields: name and age are public val fields by default.
  • Sensible toString, equals, and hashCode methods based on the class's fields.
  • An apply method, allowing you to instantiate the class without the new keyword.
  • An unapply method, making it eligible for use in pattern matching.

Instantiating a Case Class

You can instantiate a case class without the new keyword, thanks to the automatically provided apply method:

val alice = Person("Alice", 30)

Copying

Case classes come with a copy method, which is useful for creating a new instance of the class with some changed attributes while keeping the rest unchanged:

val bob = alice.copy(name = "Bob")

This creates a new Person instance with the name "Bob" and the same age as alice.

Pattern Matching

Case classes shine when used in pattern matching, thanks to their unapply method:

alice match
  case Person(name, age) => println(s"Name: $name, Age: $age")
  case _ => println("Unknown person")

This pattern matching checks if alice is a Person instance and then extracts and prints the name and age.

Companion Objects

A companion object for the case class is automatically generated, containing the apply and unapply methods among others. This means you can add additional static utility methods or values in the companion object if needed:

object Person:
  def isAdult(person: Person): Boolean = person.age >= 18

val adultCheck = Person.isAdult(alice)  // true

Use Cases

Case classes are ideal for:

  • Domain models: Representing data entities in your domain.
  • Data transfer objects (DTOs): Encapsulating data sent between processes or systems.
  • Immutable data structures: Building complex data structures that benefit from immutability.