Scala——模式匹配、异常、隐式转换、泛型

8 模式匹配

8.1 基本语法

模式匹配语法中,采用 match 关键字声明,每个分支采用 case 关键字进行声明,当需要匹配时,会从第一个 case 分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹配不成功,继续执行下一个分支进行判断。如果所有 case 都不匹配,那么会执行 case _分支,类似于 Java 中 default 语句。

(1)如果所有 case 都不匹配,那么会执行 case _ 分支,类似于 Java 中 default 语句,若此时没有 case _ 分支,那么会抛出 MatchError。
(2)每个 case 中,不需要使用 break 语句,自动中断 case。
(3)match case 语句可以匹配任何类型,而不只是字面量。
(4)=> 后面的代码块,直到下一个 case 语句之前的代码是作为一个整体执行,可以使用{}括起来,也可以不括。

package com.yu.chapter08

object test_PatternMatchBase {
  def main(args: Array[String]): Unit = {
    //1. 基本语法
    val x: Int = 2
    val y: String = x match{
      case 1 => "one"
      case 2 => "two"
      case 3 => "three"
      case _ => "other"
    }
    println(y) //two
    //2. 用模式匹配实现简单的二元运算
    val a = 25
    val b = 9
    def matchDualOp(op: Char) = op match{
      case '+' => a + b
      case '-' => a - b
      case '*' => a * b
      case '/' => a / b
      case '%' => a % b
      case _ => "非法运算符"
    }
     println(matchDualOp('+'))   //34
    println(matchDualOp('-'))    //16
    println(matchDualOp('\\'))   //非法运算符
  }
}

8.2 模式守卫

如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫。

//3. 模式守卫
def abs(num: Int): Int ={
  num match {
    case i if i >= 0 => i
    case i if i < 0 => -i
  }
}
println(abs(-8))  //8
println(abs(0))   //0

8.3 模式匹配类型

1.匹配常量
2.匹配类型
3.匹配数组
4.匹配列表
5.匹配元组

package com.yu.chapter08

object test_MatchTypes {
  def main(args: Array[String]): Unit = {
    //1.匹配常量
    def describeConst(x: Any): String = x match {
      case 1 => "Int 1"
      case "hello" => "String hello"
      case true => "Boolean true"
      case '+' => "Char +"
      case _ => ""
    }

    println(describeConst(0.1))
    println(describeConst("hello")) //String hello
    println(describeConst(true)) //Boolean true

    //2. 匹配类型
    def describeType(x: Any): String = x match {
      case i: Int => "Int " + i
      case s: String => "String " + s
      case list: List[String] => "List " + list
      case array: Array[Int] => "Array[Int] " + array.mkString(",")
      case a => "Something else " + a //这里不使用通配符是为了获取到传入的值
    }

    println(describeType(7)) //Int 7
    println(describeType("hello")) //String hello
    println(describeType(List("hi", "hello"))) //List List(hi, hello)
    println(describeType(List(2, 3))) //List List(2, 3)
    //这里不匹配List的泛型,匹配时被擦除
    // 数组例外,可保留泛型
    println(describeType(Array("hi", "hello"))) //Something else [Ljava.lang.String;@d7b1517
    println(describeType(Array(4, 5))) //Array[Int] 4,5

    //3. 匹配数组
    for (arr <- List(
      Array(0),
      Array(1, 0),
      Array(0, 1, 0),
      Array(1, 1, 0),
      Array(2, 3, 7, 29),
      Array("hello", 20, 30),
    )) {
      val result = arr match {
        case Array(0) => "0"
        case Array(1, 0) => "Array(1, 0)"
        case Array(x, y) => "Array: " + x + ", " + y //匹配两元素数组
        case Array(0, _*) => "以0开头的数组"
        case Array(x, 1, z) => "第二个元素为1的三元素数组"
        case _ => "something else"
      }
      println(result)
      //0
      //Array(1, 0)
      //以0开头的数组
      //第二个元素为1的三元素数组
      //something else
      //something else
    }

    //4. 匹配列表
    // 方式一
    for (list <- List(
      List(0),
      List(1, 0),
      List(1, 1, 0),
      List(0, 3, 5),
      List("hello")
    )){
      val result = list match {
        case List(0) => "0"
        case List(x, y) => "List(x, y): " + x + ", " + y
        case List(1, _*) => "List(1, ……)"
        case List(a) => "List(a): " + a
        case _ => "something else"
      }
      println(result)
      //0
      //List(x, y): 1, 0
      //List(1, ……)
      //something else
      //List(a): hello
    }
    //方式二
    val list = List(1, 2, 5, 7, 8)
    list match{
      case first :: second :: rest => println(s"first: $first, second $second, rest $rest")
      case _ => println("else")
    }
    //first: 1, second 2, rest List(5, 7, 8)  需要至少三个元素才能匹配上

    //5. 匹配元组
    for (tuple <- List(
      (0, 1),
      (0, 0),
      (0, 1, 0),
      (0, 1, 1),
      (1, 22, 44)
    )){
      val result = tuple match{
        case (a, b) => a + ", " + b
        case (0, _) => "(0, _)"
        case (a, 1, _) => "(a, 1, _) " + a
        case (x, y, z) => "(x, y, z): " + x + ", " + y + ", " +z
        case _ => "else"
      }
      println(result)
      //0, 1
      //0, 0
      //(a, 1, _) 0
      //(a, 1, _) 0
      //(x, y, z): 1, 22, 44
    }

    //元组匹配扩展
    //5.1 在变量声明时匹配
    val (x, y) = (10, "hello")
    println(s"x: $x, y: $y")  //x: 10, y: hello

    val List(first, second, _*) = List(23, 1, 3, 56)
    println(s"first: $first, second: $second")
    //first: 23, second: 1

    val fir :: sec :: rest = List(1, 2, 3, 4, 6)
    println(s"first: $fir, second: $sec, rest: $rest")
    //first: 1, second: 2, rest: List(3, 4, 6)

    //5.2 for推导式中进行模式匹配
    val list1: List[(String, Int)] = List(("a", 12), ("b", 13), ("c", 34), ("a", 34))

    //(1)原本的遍历方式
    for (elem <- list1) println(elem._1 + ", " + elem._2) //a, 12  b, 13  c, 34

    //(2)将list的元素直接定义为元组,对变量赋值
    for ((word, count) <- list1) println(word + ", " + count)

    //(3) 不考虑某个位置的变量,只遍历key或者value
    for ((word, _) <- list1) println(word)

    //(4)指定某个位置的值
    for (("a", count) <- list1) println(count)  //12  34
  }
}

6.匹配对象及样例类

匹配对象:

package com.yu.chapter08

object test_MatchObject {
  def main(args: Array[String]): Unit = {
    val student = new Student("alice", 8)

    //针对对象实例的内容进行匹配
    val result = student match{
      case Student("alice", 8) => "alice, 8"
      case _ => "else"
    }
    println(result) //alice, 8  匹配成功
  }
}
//定义类
class Student(val name: String, val age: Int)

//定义伴生对象  <- 因为上面case不能直接新建对象,所以创建伴生对象的apply方法
object Student {
  def apply(name: String, age: Int) = new Student(name, age)
  //必须实现一个unapply方法,用来对对象属性进行拆解 <- 只构建对象不行,还需要拆解出对象内容进行匹配对比
  //拆解Student对象,返回其属性
  def unapply(student: Student): Option[(String, Int)] = {
    if (student == null){
      None
    } else{
      Some((student.name, student.age))
    }
  }
}

匹配样例类:
样例类:Case Class
Case Class一般被翻译成样例类,它是一种特殊的类,能够被优化以用于模式匹配。当一个类被声名为case class的时候,scala会帮助我们做下面几件事情:

  • 1、构造器中的参数如果不被声明为var的话,它默认的是val类型的,但一般不推荐将构造器中的参数声明为var。
  • 2、自动创建伴生对象,同时在里面给我们实现子apply方法,使我们在使用的时候可以不直接使用new创建对象。
  • 3、伴生对象中同样会帮我们实现unapply方法,从而可以将case class应用于模式匹配。
  • 4、实现自己的toString、hashCode、copy、equals方法

除此之此,case class与其它普通的scala类没有区别。

package com.yu.chapter08

object test_MatchCaseClass {
  def main(args: Array[String]): Unit = {
    val student = Student1("alice", 8)

    //针对对象实例的内容进行匹配
    val result = student match{
      case Student1("alice", 8) => "alice, 8"
      case _ => "else"
    }
    println(result) //alice, 8     匹配成功
  }
}

//定义样例类
case class Student1(name: String, age: Int)

9 异常

1.我们将可疑代码封装在 try 块中。在 try 块之后使用了一个 catch 处理程序来捕获异常。如果发生任何异常,catch 处理程序将处理它,程序将不会异常终止。

2.Scala 的异常的工作机制和 Java 一样,但是 Scala 没有“checked(编译期)”异常, 即 Scala 没有编译异常这个概念,异常都是在运行的时候捕获处理。

3.,catch 子句是按次序捕捉的。因此,在 catch 子句中,越具体的异常越要靠前,越普遍的异常越靠后,如果把越普遍的异常写在前,把具体的异常写在后,在 Scala 中也不会报错,但这样是非常不好的编程风格。

4.finally 子句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,一般用于对象的清理工作。

5.用 throw 关键字,抛出一个异常对象。所有异常都是 Throwable 的子类型。throw 表达式是有类型的,就是 Nothing,因为 Nothing 是所有类型的子类型,所以 throw 表达式可以用在需要类型的地方。

package com.yu.chapter09

object test_Exception {
  def main(args: Array[String]): Unit = {
    try{
      val n = 10 / 0
    } catch {
      case e: ArithmeticException => {
        println("发生算术异常")
      }
      case e: Exception => {
        println("发生一般异常")
      }
    } finally {
      println("处理结束")
    }
    //发生算术异常
    //处理结束
  }
}

10 隐式转换

当编译器第一次编译失败的时候,会在当前的环境中查找能让代码编译通过的方法,用于将类型进行转换,实现二次编译。

10.1 隐式函数

隐式转换可以在不需改任何代码的情况下,扩展某个类的功能。

10.2 隐式类

使用 implicit 声明类,隐式类的非常强大,同样可以扩展类的功能,在集合中隐式类会发挥重要的作用。
(1)其所带的构造参数有且只能有一个
(2)隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是顶级的。

package com.yu.chapter09

object test_Implicit {
  def main(args: Array[String]): Unit = {
    val new12 = new MyRichInt(12)
    println(new12.Max(15))  //15

    //1.隐式函数
    implicit def convert(num: Int): MyRichInt = new MyRichInt(num)
    println(12.Max(15))  //15

    //2. 隐式类 需要定义在内部
    implicit class MyRichInt2(val self: Int){
      //自定义比较大小的方法
      def Max2(n: Int): Int = if (n < self ) self else n
      def Min2(n: Int): Int = if (n < self ) n else self
    }
    println(17.Max2(10))  //17
  }
}
//自定义类
class MyRichInt(val self: Int){
  //自定义比较大小的方法
  def Max(n: Int): Int = if (n < self ) self else n
  def Min(n: Int): Int = if (n < self ) n else self
}

10.3 隐式参数

普通方法或者函数中的参数可以通过 implicit 关键字声明为隐式参数,调用该方法时,就可以传入该参数,编译器会在相应的作用域寻找符合条件的隐式值。
(1)同一个作用域中,相同类型的隐式值只能有一个
(2)编译器按照隐式参数的类型去寻找对应类型的隐式值,与隐式值的名称无关。
(3)隐式参数优先于默认参数

11 泛型

11.1 协变和逆变

协变:Son 是 Father 的子类,则 MyList[Son] 也作为 MyList[Father]的“子类”。
逆变:Son 是 Father 的子类,则 MyList[Son]作为 MyList[Father]的“父类”。
不变:Son 是 Father 的子类,则 MyList[Father]与 MyList[Son]“无父子关系”。

class MyList[+T]{ //协变
}
class MyList[-T]{ //逆变
}
class MyList[T] //不变

11.2 泛型上下限

Class PersonList[T <: Person]{ //泛型上限
}
Class PersonList[T >: Person]{ //泛型下限
}

package com.yu.chapter09

object test_Generics {
  def main(args: Array[String]): Unit = {
    //1. 协变和逆变
    val child: Child = new Child
    val childList: MyCollection[Parent]  = new MyCollection[Child]  //这里需要做协变
    //val childList2: MyCollection[Child]  = new MyCollection[Parent]  //这里需要做逆变 [-E]

    //2. 上下限
    def test[A <: Child](a: A): Unit = {
      println(a.getClass.getName)
    }
    test[Child](new Child)  //com.yu.chapter09.Child
    test[Child](new SubChild) //com.yu.chapter09.SubChild
    //test[SubChild](new Child)  不能把父类对象赋给子类引用
  }
}
//定义继承关系
class Parent {}
class Child extends Parent {}
class SubChild extends Child {}

//定义泛型的集合类型
class MyCollection[+E] {}

11.3 上下文限定

def f[A : B](a: A) = println(a) //等同于 def f[A](a:A)(implicit arg:B[A])=println(a)

上下文限定是将泛型和隐式转换的结合产物,以下两者功能相同,使用上下文限定[A : Ordering]之后,方法内无法使用隐式参数名调用隐式参数,需要通过 implicitly[Ordering[A]]获取隐式变量,如果此时无法查找到对应类型的隐式变量,会发生出错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值