本章主要介绍Scala中的模式匹配功能。Scala中的模式匹配很有特色,语法层面上类似JAVA中的分支结构,但功能上要强得多。JAVA分支结构适用于对值作匹配,但Scala中的模式匹配可以对值、类型、集合中的元素等进行匹配,应用的场景很多。模式匹配match case的语法为:

变量v match { case => 代码 }

 

如果变量v为下划线,则表示不满足所有情况下的默认处理方式,类似JAVA分支中的default语句。其次match-case模式匹配时,只要任一个case分支满足条件,则不会继续判断下一个分支。而在JAVA中,switch-case分支会顺序执行,除非用break关键字阻断。

变量值的模式匹配示例:

def switchProtocol(code: Int) {

 code match {

     case 1 => println("HTTP")

     case 2 => println("HTTPS")

     case 3 => println("TCP")

     case _ if code > 3 => println("UDP")

     case _ => println("SMTP")

  }

}

switchProtocol(1)

 

模式匹配与变量赋值

Scala的模式匹配还支持将未匹配到的默认情况,即下划线,赋值到一个变量中,在具体处理逻辑中可以引用该变量并根据需要进行操作。对上面的例子做些改动:

def switchProtocol(code: Int) {

 code match {

   case 1 => println("HTTP")

   case 2 => println("HTTPS")

   case 3 => println("TCP")

   case _ if code > 3 => println("UDP")

   case _input => println("Not Support Code:" + _input)

  }

}

switchProtocol(-1) // 返回Not Support Code:-1

 

类型的模式匹配

如上述Scala的模式匹配除了支持值匹配之外,还能支持类型匹配。匹配类型的语法形式为:

类型匹配: case 变量:类型 => 代码

读者可以与值匹配的语法形式比较一下,仔细看分析下差异:

值匹配: case => 代码

 

下面来看一个错误处理的示例:

def onError(e: Exception) {

  ematch {

   case e1: ArrayIndexOutOfBoundsException

        =>printf("ArrayIndexOutOfBoundsException: %s", e1.getMessage)

   case e2: ClassNotFoundException

        =>printf("ClassNotFoundException: %s", e2.getMessage)

   case e3: IllegalArgumentException

        =>printf("IllegalArgumentException: %s",e3.getMessage)

   case _defult: Exception

        => printf("Exception: %s", _defult)

  }

}

onError(newArrayIndexOutOfBoundsException("Index 5 of List@1ac40 is out ofbounds."))

// 返回ArrayIndexOutOfBoundsException: Index 5 of List@1ac40 is out ofbounds.

onError(new ArithmeticException("55/0is not support."))

// 返回Exception: java.lang.ArithmeticException: 55/0 is not support.

 

关于集合的模式匹配

Scala中还能对集合执行模式匹配,可以对包含特定元素的集合、包含特定元素数量的结合以及对以某些某元素开头的集合进行匹配。下面来看几个例子:

// Array集合中的各种模式进行匹配

def contain(array: Array[Int]) {

 array match {

  // 匹配特定元素

   case Array(13) => printf("Contain element.")

  // 匹配特定数量

   case Array(e1, e2, e3) => printf("Contain 3 elements %s, %s,%s.", e1, e2, e3)

  // 匹配特定顺序

   case Array(17, _*) => printf("First with element.")

   case _ => println("Not Matched.")

  }

}

contain(Array(13)) // 返回Contain element.

contain(Array(11)) // 返回First with element.

contain(Array(11, 13, 17)) // 返回Contain 3 elements 11, 13, 17.

contain(Array(11, 13, 17, 19)) // 返回Not Matched.

 

// List集合中的各种模式进行匹配

def validate(list: List[Int]) {

 list match { // 通过::符号定义模式

  // 匹配特定元素

   case 13::Nil => printf("Contain element.")

  // 匹配特定数量

   case e1::e2::e3::Nil => printf("Contain 3 elements %s, %s,%s.", e1, e2, e3)

  // 匹配特定顺序

   case 17::tail => printf("First with element.")

   case _ => println("Not Matched.")

  }

}

validate(List(13)) // 返回Contain element.

validate(List(17)) // 返回First with element.

validate(List(11, 13, 17)) // 返回Contain 3 elements 11, 13, 17.

validate(List(11, 13, 17, 19)) // 返回Not Matched.

 

Scala中还有一种样例类的概念,有点类似于JAVA中的JavaBean的概念。在样例类中一般只定义属性,并由Scala编译时自动生成gettersetter方法,但是不包含任何方法。样例类的主构造器接收的入参,一般来说不需要使用varval修饰,Scala自动使用val修饰。此外,Scala会为样例类定义object伴生对象及apply(),该方法接收主构造器中相同入参,并返回样例类的实例。

class Command

case class RpcCommand(serviceId: String,args: String) extends Command

case class LocalCommand(serviceId: String,args: String) extends Command

// 方法中通过样例类型进行匹配

def execute(cmd: Command) {

 cmd match {

   case RpcCommand(serviceId, args)

        => printf("RPC ServiceId: %s,args: %s", serviceId, args)

    caseLocalCommand(serviceId, args)

        => printf("Local ServiceId: %s, args:%s", serviceId, args)

   case _ => println("Illegal Command.")

  }

}

execute(newRpcCommand("registService", "nginx"))

// 返回RPC ServiceId: registService, args: nginx

execute(new LocalCommand("healthCheck","pod"))

// 返回Local ServiceId: healthCheck, args: pod

 

最后来分析一下Scala中的特殊类型——OptionOption通常用来校验变量中的值是否为空。Some表示有值,None则表示值为空。示例如下:

// 定义服务注册集合

val registedSvc =Map("Authentication" -> "auth.online.com/sign={1}",

                      "uploadLog" ->"log.online.com/log={1}",

                      "healthCheck"-> "hc.online.com/node={1}")

// 根据服务名称调用指定服务

def executeSvc(svcName : String): Unit = {

 val svcAddr = registedSvc.get(svcName)

 svcAddr match {

   case Some(svcAddr) => printf("Service: %s, url: %s",svcName, svcAddr)

   case None => printf("Service %s not registed.", svcName)

  }

}

executeSvc("Authentication")

// Service: Authentication, url:auth.online.com/sign={1}

executeSvc("mail")

// Service mail not registed.

 

原文作者:江玮

版权声明:本文版权归作者(译者)及公众号所有,欢迎转载,但未经作者(译者)同意必须保留此段声明,且在文章页面明显位置给出,本文链接如有问题,可留言咨询。