第4章 Scala模式匹配、类型系统彻底精通
本章将介绍Scala模式匹配、类型系统的具体内容。
4.1模式匹配语法
模式匹配包括替代的序列,每句开始使用关键字case。一个箭头符号“=>”分开的表达模式。
语法结构:
var match {
case n=>执行语句Case n执行语句
case _=>执行语句/下划线表示通配符
}
4.2.1模式匹配基础实战
object ObjectSwitch {
def main(args: Array[String]): Unit = {
var data = "Spark"
data match {
case "Spark"=>println("Spark")
case _=>println("Nothing")//不匹配任何类型时输出Nothing
}
}
def biData(data:String): Unit ={
data match{
case "Spark"=>println("Spark")
/*if守卫与变量的使用*/
case data12 if data12 =="Flink"=>println("FLink")
case _ =>println("others")
}
}
/*类型匹配*/
def mathType(p:Person): Unit ={
case stu:Student =>"I am a student!"
case worker: Worker=>"I am a worker"
case _ =>println("Nothing")
}
}
4.2.2 数组、元祖实战
//匹配数组
def matchArray(array:Array[String])={
array match{
case Array("Scala")=>println("Scala")
case Array(spark,hadoop,storm)=>println("spark+hadoop+storm")
case Array("Spark",_*)=>println("Spark")
case _ => println("Nothing")
}
}
//
def dataTuple(tuple:Tuple2[Any,Any]): Unit ={
tuple match {
case (0,_)=>println("0 is matched")
case (y,0)=>println(y+"0")
case (x,y)=>println(x+" "+y)
}
}
4.2.3 Option实战
def dataOption(x:Option[String]) = x match {
case Some(s)=>s//匹配到Option子类some
case None => "Nothing"//匹配到Option子类None
}
4.2.4 提取器
提取器是指定义了unapply方法的object。
unapply方法接受一个数据类型,返回另一数据类型,表示可以把入参的数据解构为返回的数据。
object ExtractorTest2 {
def main(args: Array[String]): Unit = {
val x = ExtractorTest2(5)
println(x)
x match {
case ExtractorTest2(num)=>println("x bigger")
case _ => println("i cannot calculate")
}
}
def apply(x:Int) = x*2
def unapply(z:Int): Option[Int] = if(z%2 ==0)Some(z/2)else None
}
4.2.5 Scala异常处理与模式匹配
import java.io.{FileReader,FileNotFoundException,IOException}
object ExceptionTest {
def main(args: Array[String]): Unit = {
try{
val f =new FileReader("c://1.txt")
}catch{
case ex:FileNotFoundException => "Missing file exception"
case ex:IOException => "Io Exception"
}finally {
println("Exiting finally")
}
}
}
4.2.6 sealed 密封类
如果在声明超类(父类)时,加上sealed关键字,则超类就成为密封类。
密封类的好处是在做模式匹配时,编译器会自动帮助检查所有匹配项是否已完全包含。
要想添加一个匹配项,必须要写到密封类所在的里面。
//把Person类声明为密封类,则其子类都必须声明在与密封类相同的文件里。
Sealed class Person{
case class Worker extends Person
case class Student extends Person
def matchType(p:Person)=>{
p match {
case stu:Student => println("student")
case worker:Worker => println("worker")
case _ => println("Nothing")
}
}
}
4.3类型系统
4.3.1泛型
泛型就是定义以类型为参数的类或接口(Scala中为特质)的功能,在Scala中类和特质都可以带类型参数,用方括号来定义类型参数。
//泛型类
class Person[T](val content:T){
def getContent(id:T)=id+"_"+content
}
//在创建对象时已经指定了T为String类型
val p = new Person[String]("Spark")
p.getContent("Scala")
//泛型函数
def getElem[T](list:List[T]) = list(list.length-1)
4.3.2边界
泛型为Scala扩展了类及函数的复用性,但有时后也需要对泛型做限制。类型限制有类型界定,视图界定,上下文界定等。
类型界定
// T <: 上边界
class Test[T <:Comparable[T]](val first:T,val second:T){
def bigger = if(first.compareTo(second)<0)second else first
}
// T >:下边界
class Consumer[T](t:T){
def use[U>:T](u:U)={println(u)}
}
定义了T的上界Comparable,也就是说T必须是Comparable的子类。很显然不能指定T为Int 或File类型,因为在Scala中他们都不是Comparable的子类。
视图界定
类型界定中控制了T的上下界,但是当传入Int类型时,编译报错,因为Int不是上界Comparable的子类,解决办法是"视图界定"。视图界定的标识符"<%"。
// 视图界定
class Test[T <% Comparable[T]](val first:T,val second:T){
def bigger = if(first.compareTo(second)<0)second else first
}
//Ordered特质
class Test[T <% Ordered[T]](val first:T,val second:T){
def bigger=if(first<second)second else first
}
Scala就利用Ordered特质解决了大小比较这个问题。
注意:视图界定T<%Ordered[T],要求必须存在一个T到Ordered[T]隐式转换
4.3.3协变与逆变
"+“表示协变,而”-"表示逆变。
C[+T]:如果A是B的子类,那么C[A]是C[B]的子类。
C[-T]:如果A是B的子类,那么C[B]是C[A]的子类。
C[T]:无论A和B是什么关系,C[A]和C[B]没有从属关系。
协变
trait Person[+T]{
def eat
}
val children = new Person[String]{
override def eat ={
println("eat")
}
}
val father:Person[Any]=children
val action:Any= father.eat
Any是所有类的基类。
协变是正向扩展的,返回对象必然被声明对象兼容
逆变
trait Person[-]{
def eat
}
val father = new Person[Any]{
override def eat = {println("eat")}
}
val children:Person[String]= father
val action:Any = children.eat
//val action:String=children.eat会报类型不匹配
逆变是反向收缩的。实际处理类型比声明类型范围粗略,则返回对象必然会成为声明对象的兄弟对象,不能处理。
因为Any的范围比String的范围粗略,声明对象的类型不能是返回值类型的兄弟对象,否则报错。