Scala Pattern Matching
Scala offers a powerful pattern matching mechanism that is widely used.
A pattern match consists of a series of alternatives, each starting with the keyword case. Each alternative contains a pattern and one or more expressions. The arrow symbol => separates the pattern from the expressions.
Here is a simple example of integer value pattern matching:
Example
object Test {
def main(args: Array[String]) {
println(matchTest(3))
}
def matchTest(x: Int): String = x match {
case 1 => "one"
case 2 => "two"
case _ => "many"
}
}
Running the above code, the output result is:
$ scalac Test.scala
$ scala Test
many
match corresponds to Java's switch, but is written after the selector expression. That is: selector match {alternatives}.
The match expression computes by trying each pattern in the order they are written in the code. Once a matching case is found, the remaining cases will not continue to match.
Next, let's look at a pattern matching with different data types:
Example
object Test {
def main(args: Array[String]) {
println(matchTest("two"))
println(matchTest("test"))
println(matchTest(1))
println(matchTest(6))
}
def matchTest(x: Any): Any = x match {
case 1 => "one"
case "two" => 2
case y: Int => "scala.Int"
case _ => "many"
}
}
Running the above code, the output result is:
$ scalac Test.scala
$ scala Test
2
many
one
scala.Int
In the example, the first case corresponds to the integer value 1, the second case corresponds to the string value "two", the third case corresponds to the type pattern, which is used to determine whether the passed value is an integer. Using pattern matching for type checking is better than using isInstanceOf. The fourth case represents the default catch-all alternative, which is the match when no other matches are found, similar to the default in switch.
Using Case Classes
A class defined with the case keyword is a case class. Case classes are a special kind of class that is optimized for pattern matching.
Here is a simple example of a case class:
Example
object Test {
def main(args: Array[String]) {
val alice = new Person("Alice", 25)
val bob = new Person("Bob", 32)
val charlie = new Person("Charlie", 32)
for (person <- List(alice, bob, charlie)) {
person match {
case Person("Alice", 25) => println("Hi Alice!")
case Person("Bob", 32) => println("Hi Bob!")
case Person(name, age) =>
println("Age: " + age + " year, name: " + name + "?")
}
}
}
// Case class
case class Person(name: String, age: Int)
}
Running the above code, the output result is:
$ scalac Test.scala
$ scala Test
Hi Alice!
Hi Bob!
Age: 32 year, name: Charlie?
When declaring a case class, the following processes occur automatically:
Each parameter of the constructor becomes a val, unless explicitly declared as a var, but this is not recommended;
An apply method is provided in the companion object, so objects can be constructed without using the new keyword;
An unapply method is provided to make pattern matching work;
toString, equals, hashCode, and copy methods are generated, unless these methods are explicitly defined.