Scala Tutorial
Basics
Control Statements
OOP Concepts
Parameterized - Type
Exceptions
Scala Annotation
Methods
String
Scala Packages
Scala Trait
Collections
Scala Options
Miscellaneous Topics
Pattern matching is a mechanism for checking a value against a pattern and possibly deconstructing the value into its constituent parts. Scala's pattern matching is quite powerful and versatile, allowing for deep checks and intricate extraction of values from complex structures.
You can use pattern matching with the match
keyword, followed by a series of case
statements for different patterns:
val x: Int = 5 x match { case 1 => println("One") case 2 => println("Two") case 3 => println("Three") case _ => println("Something else") }
In this example, the last case _
acts as a wildcard, matching any value.
Pattern matching is especially useful with case classes:
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 }
Here, when we pass a Shape
to the area
function, Scala matches its actual type (either Circle
or Rectangle
) and extracts its parameters, allowing us to compute the area accordingly.
You can also pattern match on collections like lists:
val list = List(1, 2, 3) list match { case head :: tail => println(s"Head: $head, Tail: $tail") case Nil => println("Empty list") }
You can add conditional expressions to your patterns with if
:
val number = 5 number match { case x if x % 2 == 0 => println(s"$x is even") case x => println(s"$x is odd") }
You can match based on the type of a value:
def describeType(x: Any): String = x match { case i: Int => "Integer" case s: String => "String" case _ => "Unknown type" }
Beyond case classes, Scala allows for custom pattern matching through extractors using unapply
or unapplySeq
methods.
When a pattern introduces a new variable (like head
and tail
in the list example), it starts with a lowercase letter. If you're trying to match against an already defined variable, use backticks:
val toMatch = 5 val x = 5 x match { case `toMatch` => println("It matches!") case _ => println("Doesn't match") }
Pattern matching in Scala is an incredibly powerful tool that allows for concise, readable, and safe code when processing data structures. By leveraging this feature, developers can write more declarative code, handle various cases elegantly, and avoid many common runtime errors.
Matching on Case Classes in Scala:
Case classes are commonly used in pattern matching due to their concise structure.
case class Person(name: String, age: Int) val person: Person = Person("Alice", 25) person match { case Person("Alice", age) => println(s"Found Alice with age $age") case _ => println("Not Alice") }
Wildcard Patterns in Scala Matching:
Wildcard _
allows matching any value, useful for ignoring specific cases.
val value: Int = 42 value match { case 42 => println("The Answer") case _ => println("Not the Answer") }
Pattern Matching with Tuples in Scala:
Tuples can be used for more complex pattern matching scenarios.
val pair: (String, Int) = ("Alice", 30) pair match { case ("Alice", age) => println(s"Found Alice with age $age") case _ => println("Not Alice") }
Using Guards in Scala Pattern Matching:
Guards add conditions to pattern matches.
val age: Int = 25 age match { case x if x < 18 => println("Minor") case x if x >= 18 => println("Adult") }
Pattern Matching on Option and Some/None in Scala:
Pattern matching on Option
is a common use case for handling presence or absence of values.
val maybeValue: Option[Int] = Some(42) maybeValue match { case Some(value) => println(s"Found value: $value") case None => println("No value") }
Pattern Matching with Extractors in Scala:
Extractors allow more complex patterns using custom objects.
object Email { def unapply(email: String): Option[(String, String)] = { val parts = email.split("@") if (parts.length == 2) Some((parts(0), parts(1))) else None } } val userEmail: String = "user@example.com" userEmail match { case Email(username, domain) => println(s"Username: $username, Domain: $domain") case _ => println("Invalid email format") }
Sealed Traits and Exhaustive Pattern Matching in Scala:
Sealed traits ensure exhaustive pattern matching by allowing matches only on known subclasses.
sealed trait Shape case class Circle(radius: Double) extends Shape case class Rectangle(width: Double, height: Double) extends Shape val shape: Shape = Circle(5) shape match { case Circle(r) => println(s"Circle with radius $r") case Rectangle(w, h) => println(s"Rectangle with width $w and height $h") }
Deconstructing Values with Case Classes in Scala:
Case classes allow easy deconstruction of values during pattern matching.
case class Point(x: Int, y: Int) val point: Point = Point(3, 5) point match { case Point(x, y) => println(s"Coordinates: x=$x, y=$y") }