Scala Tutorial
Basics
Control Statements
OOP Concepts
Parameterized - Type
Exceptions
Scala Annotation
Methods
String
Scala Packages
Scala Trait
Collections
Scala Options
Miscellaneous Topics
In Scala, case classes and case objects are special constructs that simplify the creation of immutable data structures and provide various automatically derived utilities, making them extremely useful for pattern matching, decomposition, and more.
Let's look at them in more detail:
A case class
is like a regular class but with some additional benefits:
val
) by default.new
keyword to create an instance.toString
, hashCode
, and equals
: Derived automatically.case class Person(name: String, age: Int) val alice = Person("Alice", 25) val bob = alice.copy(name = "Bob") alice match { case Person(n, a) => println(s"$n is $a years old") }
A case object
is similar to a case class
, but it is a singleton, meaning it represents a single instance. It's useful when you want to represent a specific constant value that can be used in pattern matching.
sealed trait Shape case class Circle(radius: Double) extends Shape case object Square extends Shape def shapeInfo(shape: Shape): String = shape match { case Circle(r) => s"A circle with radius $r" case Square => "A square" }
sealed
keyword: When you prefix a trait or class with sealed
, it means all of its subclasses must be defined in the same file. This is very useful for pattern matching because the compiler can then check for exhaustiveness.case
prefix?: The case
prefix indicates that various utilities like unapply
, copy
, etc., are automatically generated by the compiler.Use case classes when you need a data structure with values (like a tuple but more descriptive) and case objects when you need a single, unique value to represent something. They're a staple in functional programming in Scala, especially when combined with pattern matching.
Introduction to Case Classes and Case Objects in Scala:
Case classes and case objects are special classes in Scala designed for immutable data modeling. They come with various features like automatic companion objects, pattern matching support, and more.
case class Person(name: String, age: Int) case object EmptyPerson
Pattern Matching with Case Classes and Case Objects in Scala:
Pattern matching is simplified with case classes and case objects.
def processPerson(person: Person): String = person match { case Person("Alice", _) => "Hello, Alice!" case Person(_, age) if age < 18 => "You are a minor." case _ => "Greetings!" }
Immutable Data Structures with Case Classes in Scala:
Case classes are inherently immutable, providing a clean and safe way to represent data.
val alice = Person("Alice", 25) val olderAlice = alice.copy(age = 30)
The copy
method creates a new instance with modified fields, preserving immutability.
Case Objects vs Case Classes in Scala:
Case objects are like case classes but with no companion objects and no constructor parameters. They are often used to represent singletons.
case class Circle(radius: Double) case object EmptyCircle
EmptyCircle
is a singleton representing an empty circle.
Deriving Equality and Hash Code with Case Classes in Scala:
Case classes automatically generate equals
and hashCode
methods based on their fields.
val person1 = Person("Alice", 25) val person2 = Person("Alice", 25) println(person1 == person2) // Result: true
The equality check is based on the values of the fields.
Sealed Hierarchies and Case Classes in Scala:
When used in a sealed hierarchy, case classes enable exhaustive pattern matching.
sealed trait Shape case class Circle(radius: Double) extends Shape case class Rectangle(width: Double, height: Double) extends Shape def area(shape: Shape): Double = shape match { case Circle(r) => math.Pi * r * r case Rectangle(w, h) => w * h }
Adding the sealed
modifier ensures that all subclasses are declared in the same file, enabling the compiler to warn if pattern matching is non-exhaustive.