Skip to Content
Course content
Click on the "Edit" button in the top corner of the screen to edit your slide content.

In a lot of languages, you spend a huge chunk of your time writing "boilerplate" code—getters, setters, equals(), hashCode(), and toString()—just to create a simple object that holds some data. I used to do this in Java for years, and it's tedious. In Scala, we have case classes, which essentially tell the compiler: "This class is primarily a container for data, so just handle all that boring stuff for me."

The frustration of regular classes for data

To show you why case classes are a lifesaver, let's try to build a simple system to track coffee orders. I'll start by doing it the "wrong" way—using a standard class—so you can see the headache I'm talking about.

class Order(val id: Int, val item: String)

val order1 = Order(1, "Latte")
val order2 = Order(1, "Latte")

println(order1 == order2) // This prints: false

Wait, why false? The IDs are the same, the items are the same. But because Order is a regular class, Scala uses reference equality. It's checking if these are the exact same object in memory, not if they contain the same data. If I wanted this to return true, I'd have to manually override the equals and hashCode methods. It's a lot of typing for very little gain.

Switching to Case Classes

Now, let's fix this by simply adding the case keyword. This changes everything. A case class automatically implements value-based equality, a useful toString implementation, and it makes the constructor parameters public vals by default.

case class Order(id: Int, item: String)

val order1 = Order(1, "Latte")
val order2 = Order(1, "Latte")

println(order1 == order2) // This prints: true
println(order1)           // This prints: Order(1,Latte)

Much better. I didn't have to write a single line of logic to make that work. Also, notice I didn't use the new keyword to create the objects. Case classes provide an apply method automatically, making the syntax cleaner.

Updating data without mutating it

In Scala, we prefer immutability. You shouldn't be changing the properties of an order once it's created; instead, you create a new version of that order with the change. You might think this is inefficient, but the copy method makes it trivial.

Let's say the customer decided they wanted a "Large Latte" instead of just a "Latte".

val originalOrder = Order(1, "Latte")
val updatedOrder = originalOrder.copy(item = "Large Latte")

println(originalOrder) // Order(1,Latte) - still intact!
println(updatedOrder)  // Order(1,Large Latte)

The copy method creates a new instance, copying all existing fields except the ones you explicitly change. It's a pattern I use constantly to keep my state predictable and bug-free.

Making the data useful with Pattern Matching

The real "superpower" of case classes unlocks when you combine them with pattern matching. Because the compiler knows the structure of a case class, it can "deconstruct" it to get to the data inside. This is called unapply.

Imagine we want to print a custom message based on the order item:

def processOrder(order: Order): String = order match {
  case Order(_, "Latte") => "Steaming the milk for a Latte!"
  case Order(id, item)   => s"Processing order #$id for a $item"
}

println(processOrder(Order(1, "Latte"))) // Steaming the milk for a Latte!
println(processOrder(Order(2, "Espresso"))) // Processing order #2 for a Espresso

In the first case, I used an underscore _ because I didn't care about the ID—I only cared if the item was a "Latte". In the second case, I captured both the ID and the item to use them in the string. This is infinitely more readable than a chain of if/else statements checking properties.




📋 Practical Task

Build a Digital Library Inventory System

You need to create a small system to manage books in a library. Your task is to implement the following using the concepts from this lesson:

  • Create a case class called Book with three properties: isbn: String, title: String, and isAvailable: Boolean.
  • Create a function called checkoutBook(book: Book). This function should take a Book and return a new Book instance where isAvailable is set to false using the copy method.
  • Create a function called bookStatus(book: Book) that uses pattern matching to return a string:
    • If the book is available, return: "The book [title] is ready for pickup."
    • If the book is not available, return: "Sorry, [title] is currently checked out."

Test your code by creating a book, checking it out, and printing the status of both the original and the updated book objects.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.