Scala之模式匹配、异常、隐式转换以及泛型

一、模式匹配

1.1 基本语法

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

  def main(args: Array[String]): Unit = {

    var a: Int = 10
    var b: Int = 20
    var operator: Char = 'd'

    var result = operator match {
      case '+' => a + b
      case '-' => a - b
      case '*' => a * b
      case '/' => a / b
      case _ => "illegal"
    }

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

1.2 模式守卫

  • 说明:如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫。
  • 案例
object TestMatchGuard {

    def main(args: Array[String]): Unit = {

        def abs(x: Int) = x match {
            case i: Int if i >= 0 => i
            case j: Int if j < 0 => -j
            case _ => "type illegal"
        }

        println(abs(-5))
    }
}

1.3 模式匹配类型

1.3.1 匹配常量

  • 说明:Scala中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等
  • 案例
object TestMatchVal {

    def main(args: Array[String]): Unit = {

        println(describe(6))

    }

    def describe(x: Any) = x match {

        case 5 => "Int five"

        case "hello" => "String hello"

        case true => "Boolean true"

        case '+' => "Char +"

    }
}

1.3.2 匹配类型

  • 需要进行类型判断时,可以使用前文所学的isInstanceOf[T]和asInstanceOf[T],也可使用模式匹配实现同样的功能。
  • 案例
object TestMatchClass {

    def describe(x: Any) = x match {

        case i: Int => "Int"
        case s: String => "String hello"
        case m: List[_] => "List"
        case c: Array[Int] => "Array[Int]"
        case someThing => "something else " + someThing
    }

    def main(args: Array[String]): Unit = {

        //泛型擦除,都是List
        println(describe(List(1, 2, 3, 4, 5)))

        //数组例外,可保留泛型,Array[Int]
        println(describe(Array(1, 2, 3, 4, 5, 6)))
        println(describe(Array("abc")))
    }
}

1.3.3 匹配数组

  • scala模式匹配可以对集合进行精确的匹配,例如匹配只有两个元素的、且第一个元素为0的数组。
  • 案例
object TestMatchArray {

    def main(args: Array[String]): Unit = {

        for (arr <- List(Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0), Array(1, 1, 0, 1), Array("hello", 90))) { // 对一个数组集合进行遍历

        val result = arr match {
            case Array(0) => "0" //匹配Array(0) 这个数组

            case Array(x, y) => x + "," + y //匹配有两个元素的数组,然后将将元素值赋给对应的x,y

            case Array(0, _*) => "以0开头的数组" //匹配以0开头和数组

            case _ => "something else"
      }

      println("result = " + result)
}

1.3.4 匹配列表

  • 方式一
bject TestMatchList {
    def main(args: Array[String]): Unit = {

        //list是一个存放List集合的数组
        //请思考,如果要匹配 List(88) 这样的只含有一个元素的列表,并原值返回.应该怎么写
        for (list <- Array(List(0), List(1, 0), List(0, 0, 0), List(1, 0, 0), List(88))) {

            val result = list match {

                case List(0) => "0" //匹配List(0)
                case List(x, y) => x + "," + y //匹配有两个元素的List
                case List(0, _*) => "0 ..."
                case _ => "something else"
            }

            println(result)
        }
    }
}
  • 方式二
object TestMatchList {

    def main(args: Array[String]): Unit = {

        val list: List[Int] = List(1, 2, 5, 6, 7)

        list match {
            case first :: second :: rest => println(first + "-" + second + "-" + rest)
            case _ => println("something else")
        }
    }
}

1.3.5 匹配元组

  • 案例
package tiger.scala.chapter08

object Scala04_TestMatch {
  def main(args: Array[String]): Unit = {
//    val list = List(("a", 1), ("b", 2), ("c", 3))

    /*// 对list集合进行遍历,输出元组中的第一个元素
    for (elem:(String, Int) <- list) {
      println(elem._1)
    }*/

    // 特殊的模式匹配1
    /*for ((word, count) <- list) {
      println(word)
    }*/
    /*for ((word, _) <- list) {
      println(word)
    }*/

    /*for (("a", count) <- list) {
      println(count)
    }*/

    // 特殊模式匹配2 在模式匹配的时候,给元组元素命名
    /*val (id, name, age):(Int, String, Int) = (100, "jingjing", 10)
    println(name)*/

    // 元组key不变,value*2
    val list = List(("a", 1), ("b", 2), ("c", 3))
    /*list.map(
      t => {
        t match {
          case (word, count) => (word, count * 2)
        }
      }
    )*/  // 简化

    // 如果匿名函数中使用模式匹配case,要求必须使用花括号括起来
    // 如果一个函数中只有一个参数,那么参数列表的小括号可以用花括号代替
    val newList: List[(String, Int)] = list.map { case (word, count) => (word, count * 2) }
    println(newList)

    // 练习:使用模式匹配:对count * 2
    val list1: List[(String, (String, Int))] = List(("a", ("a", 5)), ("b", ("b", 10)), ("c", ("c", 20)))
    val newList1: List[(String, (String, Int))] = list1.map {
      case (word1, (word2, count)) => (word1, (word2, count * 2))
    }
    println(newList1)


  }
}

1.3.6 匹配对象及样例类

  • 基本语法:
    • 说明
      • val user = User("zhangsan",11),该语句在执行时,实际调用的是User伴生对象中的apply方法,因此不用new关键字就能构造出相应的对象。
      • 当将User("zhangsan", 11)写在case后时[case User("zhangsan", 11) => "yes"],会默认调用unapply方法(对象提取器),user作为unapply方法的参数,unapply方法将user对象的name和age属性提取出来,与User("zhangsan", 11)中的属性值进行匹配
      • case中对象的unapply方法(提取器)返回Some,且所有属性均一致,才算匹配成功,属性不一致,或返回None,则匹配失败。
      • 若只提取对象的一个属性,则提取器为unapply(obj:Obj):Option[T]
      • 若提取对象的多个属性,则提取器为unapply(obj:Obj):Option[(T1,T2,T3…)]
      • 若提取对象的可变个属性,则提取器为unapplySeq(obj:Obj):Option[Seq[T]]
class User(val name: String, val age: Int)

object User{

    def apply(name: String, age: Int): User = new User(name, age)

    def unapply(user: User): Option[(String, Int)] = {
        if (user == null)
            None
        else
            Some(user.name, user.age)
    }
}

object TestMatchUnapply {
    def main(args: Array[String]): Unit = {
        val user: User = User("zhangsan", 11)
        val result = user match {
            case User("zhangsan", 11) => "yes"
            case _ => "no"
        }

        println(result)
    }
}
  • 样例类
    • 语法:case class Person (name: String, age: Int)
  • 说明
    • 样例类仍然是类,和普通类相比,只是其自动生成了伴生对象,并且伴生对象中自动提供了一些常用的方法,如applyunapplytoString、equals、hashCode和copy。
    • 样例类是为模式匹配而优化的类,因为其默认提供了unapply方法,因此,样例类可以直接使用模式匹配,而无需自己实现unapply方法。
    • 构造器中的每一个参数都成为val,除非它被显式地声明为var(不建议这样做)
  • 案例
package tiger.scala.chapter08

/*
* 样例类
*
* */

case class Student(var name:String, var age:Int) {

}

object Scala06_TestMatch {
  def main(args: Array[String]): Unit = {
    val std1 = Student("jingjing", 18)
    val res = std1 match {
      case Student("jingjing", 18) => "success"
      case _ => "fail"
    }
    println(res)
  }
}

二、异常

package tiger.scala.chapter09

/*
* 异常处理
* Java的异常
*   异常的体系结构
*     Throwable
*       Error
*       Exception
*         编译时异常:编译阶段进行处理
*         运行时异常:运行阶段进行处理,继承RuntimeException
*   异常的执行原理
*     程序运行过程中,如果发生了异常,那么底层会创建对应的异常对象,通过throw关键字,将异常向上抛出
*     抛出:JVM会找能够进行异常处理的代码(对异常进行捕获)
*
*   异常处理的方式
*     通过throws声明异常
*     通过try...catch 进行异常捕获
*     try {
*       可能发生异常的代码
*     } catch (异常类型 变量名) {
*       范围小的异常  异常处理的代码
*     } catch (异常类型 变量名) {
*       范围大的异常  异常处理代码
*     } finally {
*       不管是否发生异常都会执行的异常
*       一般用于资源的释放
*     }
*
* Scala异常
*   Scala中不区分编译时异常以及运行时异常
*   Scala在进行异常捕获的时候,只有一个catch块,在catch块中,通过模式匹配,匹配不同类型的异常
*     而且在进行匹配,就算是将大类型的异常放在前面,也不会报错(但是我们不建议这么做)
*   通过注解@Throws注解标记方法可能会发生异常
*
*
* */

object Scala01_TestException {
  def main(args: Array[String]): Unit = {
    try {
      // 可能发生异常的代码
      10 / 1
      println("程序正常执行")
    } catch {
      // 异常处理的代码
      case e:Exception => println("发生异常了" + e.getMessage)
    } finally {
      // 不管是否发生异常都会执行
      println("finally被执行了")
    }
    println("异常之外的代码")
  }
/*  def ff() = {
    throw NullPointerException
  }*/
}

三、隐式转换

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

3.1 隐式函数

  • 说明
    • 隐式转换可以在不需改任何代码的情况下,扩展某个类的功能。
  • 案例
class MyRichInt(val self: Int) {
    def myMax(i: Int): Int = {
        if (self < i) i else self
    }

    def myMin(i: Int): Int = {
        if (self < i) self else i
    }
}

object TestImplicitFunction {
    // 使用implicit关键字声明的函数称之为隐式函数
    implicit def convert(arg: Int): MyRichInt = {
        new MyRichInt(arg)
    }

def main(args: Array[String]): Unit = {
    // 当想调用对象功能时,如果编译错误,那么编译器会尝试在当前作用域范围内查找能调用对应功能的转换规则,这个调用过程是由编译器完成的,所以称之为隐式转换。也称之为自动转换
        println(2.myMax(6))
    }
}

3.2 隐式参数

  • 普通方法或者函数中的参数可以通过implicit关键字声明为隐式参数,调用该方法时,就可以传入该参数,编译器会在相应的作用域寻找符合条件的隐式值。
  • 说明
    • 同一个作用域中,相同类型的隐式值只能有一个
    • 编译器按照隐式参数的类型去寻找对应类型的隐式值,与隐式值的名称无关。
  • 案例
object TestImplicitParameter {

    implicit val str: String = "hello world!"

    def hello(implicit arg: String="good bey world!"): Unit = {
        println(arg)
    }

    def main(args: Array[String]): Unit = {
        // 使用隐式参数的函数在调用的时候不能加()
        hello
    }
}

3.3 隐式类

  • 在Scala2.10后提供了隐式类,可以使用implicit声明类,隐式类的非常强大,同样可以扩展类的功能,在集合中隐式类会发挥重要的作用。
  • 隐式类说明
    • 其所带的构造参数有且只能有一个
    • 隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是顶级的
  • 案例
package tiger.scala.chapter10


object Scala01_TestImplic {



  def main(args: Array[String]): Unit = {
    println(2.jMax(5))
  }
  implicit class JRichInt(var self:Int) {
    def jMax(i: Int): Int = {
      if (self < i) i else self
    }

    def JMin(i:Int): Int = {
      if (self < i) self else i
    }
  }
}

3.4 隐式解析机制

  • 说明
    • 首先会在当前代码作用域下查找隐式实体(隐式方法、隐式类、隐式对象)。(一般是这种情况)
    • 如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。类型的作用域是指与该类型相关联的全部伴生对象以及该类型所在包的包对象
  • 案例
package com.atguigu.chapter10

import com.atguigu.chapter10.Scala05_Transform4.Teacher

//(2)如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。类型的作用域是指与该类型相关联的全部伴生模块,
object TestTransform extends PersonTrait {

    def main(args: Array[String]): Unit = {

        //(1)首先会在当前代码作用域下查找隐式实体
        val teacher = new Teacher()
        teacher.eat()
        teacher.say()
    }

    class Teacher {
        def eat(): Unit = {
            println("eat...")
        }
    }
}

trait PersonTrait {

}

object PersonTrait {
    // 隐式类 : 类型1 => 类型2
    implicit class Person5(user:Teacher) {

        def say(): Unit = {
            println("say...")
        }
    }
}

四、泛型

package tiger.scala.chapter11

/*
* 泛型
* */

class Parent{}
class Child extends Parent{}
class SubChild extends Child{}

// 泛型模版
// 不可变性
// class MyList[T] {}

// 协变
// class MyList[+T] {}

// 逆变
class MyList[-T]{}

object Scala01_TestGeneric {
  def main(args: Array[String]): Unit = {
    // 不可变性
    // var s:MyList[Child] = new MyList[Child]

    // 协变
    // var s:MyList[Child] = new MyList[SubChild]

    // 逆变
//    var s:MyList[Child] = new MyList[Parent]

    test(classOf[Child])
    test(classOf[SubChild])

  }

  // >:表示泛型通配符下界
  /*def test[A >: Child](a:Class[A]): Unit = {
    println(a)
  }*/

  // <:表示泛型通配符上界
//  def test[A <: Child](a:Class[A]): Unit = {
//    println(a)
//  }

  def test[A >: Child](a:A): Unit ={

  }


}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值