简体   繁体   中英

What is the advantage of using scala pattern matching instead of java switch case?

Everybody says that pattern matching is a great feature in functional languages. Why?

Can't I simple use ifs and switch cases for everything?

I'd like to understand the advantages of using pattern matching instead of regular procedural programming ifs and switch cases

I'd first like to note that you don't use pattern matching "instead" of switch statements. Scala doesn't have switch statements, what it does have is match blocks, with cases inside that superficially look very similar to a switch statement.

Match blocks with pattern matching does everything that switch does, and much more.

A) It's not restricted to just primitives and other types that Oracle have chosen to "bless" in the language spec (Strings and Enums). If you want to match on your own types, go right ahead!

B) Pattern matching can also extract . For example, with a tuple:

val tup = ("hello world", 42)
tup match {
  case (s,i) =>
    println("the string was " + s)
    println("the number was " + i
}

With a list:

val xs = List(1,2,3,4,5,6)
xs match {
  case h :: t =>
    // h is the head: 1
    // t is the tail: 2,3,4,5,6
    // The :: above is also an example of matching with an INFIX TYPE
}

With a case class

case class Person(name: String, age: Int)
val p = Person("John Doe", 42)
p match {
  case Person(name, 42) =>
    //only extracting the name here, the match would fail if the age wasn't 42
    println(name)
}

C) pattern matching can be used in value assignment and for-comprehensions , not just in match blocks:

val tup = (19,73)

val (a,b) = tup

for((a,b) <- Some(tup)) yield a+b // Some(92)

D) match blocks are expressions, not statements

This means that they evaluate to the body of whichever case was matched, instead of acting entirely through side-effects. This is crucial for functional programming!

val result = tup match { case (a,b) => a + b }

Somehow my edit/addition to @KevinWright answer got thrown away, so I'll add it here as one more nice pattern matching feature...

F) Compiler exhaustiveness check of cases.

If there exists a value matching against which will not be covered by existing cases compiler will warn you about it. This is a very nice feature of the language because if you don't ignore these compiler warnings you won't catch such runtime exceptions or come across a case you didn't think of. If you still run the application and ignore the warning you will get a nice descriptive exception if your value does not match any cases. Here is an illustration:

scala> def badMatch(l: List[Int]): Unit = l match { case x :: xs => println(x) }
<console>:7: warning: match may not be exhaustive.
It would fail on the following input: Nil
       def badMatch(l: List[Int]): Unit = l match { case x :: xs => println(x) }
                                          ^
badMatch: (l: List[Int])Unit

scala> badMatch(List(1, 2))
1

scala> badMatch(Nil)
scala.MatchError: List() (of class scala.collection.immutable.Nil$)

I prefer to get an exception in this case because it will fail loud and clear, and usually early instead of executing unexpected branches of logic.

If you use if you would have to use else , and if you use Java switch you would have to have default case to cover all cases. But notice the difference: Scala compiler knows that your empty list is different from non-empty list in this case, or in more broad sense you define the granularity of matches. You could match lists with 1 or 2 elements and ignore the rest, or use any other much more complex patterns without having to worry if you managed to cover all cases.

In short as you use complex extraction and matching logic compiler will make sure you didn't miss any cases. There is nothing similar in Java unless you use a default case like default or else .

pattern matching is not somehow an alternative of switch statement , I consider it to be another way of doing dynamic dispatch in oop. They try to do the same thing: calling a different version of the function based on the dynamic type of the arguments

As it was written in the other answers, Scala pattern matching and Java switch do not make the same thing.

switch statement:

  • Only works with native types, enumerated types, and the String class
  • It is an alternative to a chain of "if-else" to create multiple execution paths, according to an imperative programming

pattern matching:

  • It Allows to match on any sort of data with a first-match policy
  • It meets a functional logic: each case statement returns a value and the whole match statement is virtually a function that returns the matching case value.

In other words, you can use the "pattern matching" for a similar purpose of the "java switch", but in doing so you're using functional tools in an imperative way.

JMPL is simple java library, which could emulate some of the features pattern matching, using Java 8 features.

      matches(data).as(
          new Person("man"),    () ->  System.out.println("man");
          new Person("woman"),  () ->  System.out.println("woman");
          new Person("child"),  () ->  System.out.println("child");        
          Null.class,           () ->  System.out.println("Null value "),
          Else.class,           () ->  System.out.println("Default value: " + data)
       );


       matches(data).as(
          Integer.class, i  -> { System.out.println(i * i); },
          Byte.class,    b  -> { System.out.println(b * b); },
          Long.class,    l  -> { System.out.println(l * l); },
          String.class,  s  -> { System.out.println(s * s); },
          Null.class,    () -> { System.out.println("Null value "); },
          Else.class,    () -> { System.out.println("Default value: " + data); }
       );

       matches(figure).as(
          Rectangle.class, (int w, int h) -> System.out.println("square: " + (w * h)),
          Circle.class,    (int r)        -> System.out.println("square: " + (2 * Math.PI * r)),
          Else.class,      ()             -> System.out.println("Default square: " + 0)
       );

Taken from a great book Programming in Scala by Martin Odersky (creator of Scala) and others.

Java's switch

  • integer-type, enum and constants can be used can be used in case
  • break at the end of each alternative
  • fall through from one alternative to the next without break
  • doesn't result in a value

Scala's match

  • any kind of constant, as well as other things, can be used in case
  • no break at the end of each alternative
  • no fall through from one alternative to the next
  • results in a value

Exampes of the same logic programmed in Java and Scala:

Scala

firstArg match
  case "salt" => println("pepper")
  case "chips" => println("salsa")
  case "eggs" => println("bacon")
  case _ => println("huh?")

Java

switch (firstArg) {
  case "salt":
    System.out.println("pepper");
    break;
  case "chips":
    System.out.println("salsa");
    break;
  case "eggs":
    System.out.println("bacon");
    break;
  default:
    System.out.println("huh?");
}

Scala's match can results in a value , Java's switch cannot:

friend match
  case "salt" => "pepper"
  case "chips" => "salsa"
  case "eggs" => "bacona"
  case _ => "huh?"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM