Today I want to write about a dream of every Java developer – pattern matching. While a switch statement works only with limited number of types, including primitive values, strings and enums, the pattern matching feels confident with almost any argument type. I’m going to demonstrate a batch of examples where pattern matching is useful.

There are a lot of cases when you need to make a decision in a program based on some value. The first thought which comes to mind is IF ELSE operator or Java SWITCH statement. But Scala offers more powerful and elegant approach for handling such scenarios. Let’s consider how pattern matching can help you.

Here is a simple example of pattern matching:

1 + 2 - 3 + 4 - 5 match {
  case -1 => "minus one"
  case 0  => "zero"
  case 1  => "one"
  case _  => "unknown" 
} 
//minus one

In the first line we define an algebraic expression, then we use a special keyword match. After that follows curly bracket. In lines 2, 3, 4 and 5 we put possible patterns. They represent cases which we expect to get as a result of the expression evaluation. Also pay extra attention to the underscore symbol. It means everything else or default pattern. Finally curly bracket is closed.

Summing up here is a general syntax for a pattern matching:

pattern-matching-syntax

We can match any expression, then in case sections we describe what we expect to get. If the expression matches some particular case, pattern matching stops and returns expression which corresponds to the matched case. If there is no any match, then MatchError exception is thrown. That’s why, as a rule, in the last case line developers put underscore (I mentioned it before), it serves as default expression.

Pattern matching examples

Let’s examine several samples where pattern matching is used. The first example is the most simple. We are going to match expression to constants.

val boo: Boolean = 5 < 10

boo match {
  case true => 1
  case false => 0
}
//1

In the next example we put a pattern matching in a function and use underscore in case when all previous patterns are not matched.

def matchFunction(v: Int) = v match {
  case 1 => "one"
  case 2 => "two"
  case _ => "unknown number"
}

matchFunction(2) //two
matchFunction(5) //unknown number

Well, now we can continue with more complex samples. Let’s apply pattern matching to case classes:

trait Payment {
  def pay(amount: Double): Unit
}

class Cash extends Payment {
  def pay(amount: Double): Unit = println(s"Pay with cash $amount")
}
case class CreditCard(fullName: String) extends Payment {
  def pay(amount: Double): Unit = println(s"Pay with credit card $amount")
  def verify(): Unit = println("Verification...")
}

def processPayment(amount: Double, method: Payment) = method match {
  case cash: Cash => cash.pay(amount)
  case card: CreditCard => {
    card.verify()
    card.pay(amount)
  }
  case _ => println("Unknown payment method")
}

val paymentA = new Cash
val paymentB = new CreditCard("Alex Zvolinskiy")

processPayment(10, paymentA)
//Pay with cash 10.0

processPayment(50, paymentB)
//Verification...
//Pay with credit card 50.0

This time we matched patterns and then used them. In order to do this you can simply use following construction:

...
  case aliasName: SomeType => aliasName.particularMethod()
...

There is one more way to write pattern matching expressions:

trait Alcohol

case class Beer(price: Double, name: String) extends Alcohol
case class Wine(price: Double, name: String, year: Int) extends Alcohol

def buyAlcohol(alco: Alcohol) = alco match {
  case Beer(price, name) =>
    println(s"You pay $price for $name")
  case Wine(price, name, year) =>
    println(s"You pay $price for $name ($year)")
}

As you see now we match patterns by type parameters: Beer(price, name) and Wine(price, name, year). This approach is helpful when you want to operate exactly with the pattern arguments.

Based on these examples we can cover most part of the real life scenarios. Furthermore pattern matching can be nested. But as in case with IF-ELSE when level of nesting exceeds 2, code become hard to read.

Collections & pattern matching

What is really cool is that you can apply pattern matching even to collections. It’s very convenient to deal with collection elements using pattern matching. Here is basic example:

val commonList = List(1, 2, 3, 4, 5)
val emptyList = Nil
val oneElement = 'a' :: Nil

def checkList[T](list: List[T]): String = list match {
  case Nil => "Empty list"
  case list :: Nil => "One element"
  case _ => "More than one element"
}

checkList(emptyList) //Empty list
checkList(oneElement) //One element
checkList(commonList) //More than one element

Here is how you can use pattern matching in a recursive function for summing of integers.

val commonList = List(1, 2, 3, 4, 5)

def sum(list: List[Int]): Int = {
  def recursion(sum: Int, list: List[Int]): Int = list match {
    case Nil => sum
    case el :: tail => (recursion(sum + el, tail))
  }
  recursion(0, list)
}

sum(commonList) //15

For-expressions & pattern matching

I bet you thought that all possible cases where pattern matching could be used are finished. But I still have a couple of them. So let’s consider a for-expression:

case class Car(pureWeight: Int, equipment: Int)

val cars = Seq(Car(1600, 250), Car(1900, 140), Car(1650, 200))

for (Car(weight, eqWeight) <- cars) yield {
  weight + eqWeight
}
//List(1850, 2040, 1850)

Here we calculated total weight of cars with equipment. Then we can use these values for example in some checkWeightRestriction(weight: Int) function.

Finally I want to show the most smallest pattern matching example:

val pair = ("Alex", "Texas")

pair match {case (name, city) => s"Welcome $name from $city"}

We can disintegrate almost everything in a pattern and then use it in a convenient way for us. That’s why pattern matching so useful in Scala. It’s very handy to operate with values without casting them using instanceOf etc. Also it’s much more readable than IF-ELSE chains.

I described the most crucial and popular examples where a pattern matching applies. I’m sure that I missed some more rare scenarios of pattern matching, so I’m waiting for these examples in comments. You are welcome to write your opinion!

About The Author

Mathematician, programmer, wrestler, last action hero... Java / Scala architect, trainer, entrepreneur, author of this blog

Close