揭开Scala的糖衣(8) -- pattern matching
Pattern matching是Scala中很好用的一个语言特性。先举一个最简单的例子:
1 | val number = 1 |
这个代码和我们熟悉的switch case看起来很像,其实,这段代码反编译之后和Java的switch case确实就是一样的:
1 | int number = 1; |
但是和Java的switch case不一样的是,Scala的pattern matching作为一个expression是可以evaluate一个值出来的,我们把上面的代码改一下,让doSomething,doSomethingElse和doDefault都返回点东西:
1 | val number = 1 |
这样,result就承接了能够match上的那个case的返回值。而无需像普通的swtich case一样在每个case中给result赋值。
单是这样看,pattern matching的魅力还不算怎么大,我们再看一下下面这个例子:
1 | abstract class Animal |
首先声明几个case classes。这些case classes会被编译成一些比较复杂的classes,我们暂时不去关心。
然后看一下如何match类型及其属性:
1 | val animal = createAnimal |
这段代码很容易懂,如果创建出来的animal是狗的话,无论它的名字叫什么,我们都返回this is a dog,如果是一只名叫kitty的猫,则返回this is a cat named kitty。如果都不是的话,则返回other animal。
很简单的几行代码,就做出了类型判断而且还有属性判断。
如果没有pattern matching,那么就要写if去判断类型,如果类型符合还要做类型转换,然后把转换后的变量中的属性取出来,再然后才能对属性的值做判断,最后才能返回点东西……
类型判断,类型转换,取属性,属性值判断,返回值。这么五件事我们用这样一行代码就都解决了:
1 | case Cat("kitty") => "this is a cat named kitty" |
这样的Scala代码会被编译成什么样呢?其实就是我们上面描述的很复杂的样子:
1 | Animal animal; |
这段反编译出来的代码不很可读。我们就凑合着粗看一下。里面和我们前面说的一样,都是if else,类型判断,转型,判等……
当然,用反编译工具给出的Java代码和上面的Scala代码作比较并不公平。我们自己把它写一遍:
1 | Animal animal = createAnimal(); |
这个样子再和上面的Scala代码比较,可以看到Scala编译器帮我们省掉了局部变量,类型判断和判等这些噪音。
Pattern matching还有很多其他用法,比如用来match tuple:
1 | val hostPort = ("localhost", 80) |
或者是用来match option:
1 | val map = Map(1 -> "one", 2 -> "two") |
Scala标准库中的Map的get方法的返回类型是Option,如果能够get到东西则返回Some,其中包着get到的值。如果get不到东西,则返回一个None。
由于Tuple和Option本身也是case class,所以上面的两段代码反编译出来和上面的Java代码是大同小异的。就不再赘述了。