Case Classes
Prerequisites: Classes
A case class is a class for immutable data. One line gives you a
constructor, read-only fields, value-based equality, a readable
toString, a copy method, and pattern matching support.
case class Point(x: Int, y: Int)
@main def points(): Unit =
val a = Point(1, 2)
val b = Point(1, 2)
println(a)
println(a.x)
println(a == b)
println(a.copy(y = 10))
println(Set(a, b).size)
Point(1,2)
1
true
Point(1,10)
1
- Parameters are automatically
valfields. ==compares contents: two points with the same coordinates are equal, and they deduplicate in aSet.copycreates a changed copy — case classes are updated by making new values, not by mutation.
Modeling a domain
case class Address(city: String, country: String)
case class Customer(name: String, email: String, address: Address)
case class Order(id: Int, customer: Customer, total: BigDecimal)
@main def domain(): Unit =
val ada = Customer("Ada", "ada@example.com", Address("London", "UK"))
val order = Order(1, ada, BigDecimal("59.97"))
println(order.customer.address.city)
val moved = order.copy(customer = ada.copy(address = Address("Paris", "FR")))
println(moved.customer.address)
println(order.customer.address)
London
Address(Paris,FR)
Address(London,UK)
The original order is unchanged, so it can be shared safely.
Pattern matching on case classes
case class Point(x: Int, y: Int)
def describe(p: Point): String = p match
case Point(0, 0) => "origin"
case Point(x, 0) => s"on the x-axis at $x"
case Point(0, y) => s"on the y-axis at $y"
case Point(x, y) => s"at ($x, $y)"
@main def matching(): Unit =
List(Point(0, 0), Point(3, 0), Point(0, -2), Point(4, 5))
.map(describe)
.foreach(println)
origin
on the x-axis at 3
on the y-axis at -2
at (4, 5)
Patterns take the value apart into its fields — the Pattern Matching lesson goes further.
Common mistakes
- Using
varfields in case classes. It's allowed, but it breaks the guarantees that make them useful (for example, a changed field changes the hash code of a value already in aSet).