模式匹配隐式转换

30
五月
2021

文章目录

      • 1 .模式匹配基本概念
            •        1.1 什么是模式匹配
            •        1.2 基本语法
      • 2.模式匹配--匹配种类
            •        2.1 匹配常量: 数字 字符 字符串
            •        2.2 匹配类型: 整形 字符型 集合类型
            •        2.3 匹配具体的集合元素
            •        2.4 匹配对象
            •        2.5 匹配范围
      • 3.模式匹配在变量声明中的使用
      • 4.隐式转换
            •        4.1 什么是隐式转化
            •        4.2 什么情况下会进行隐式转化
            •        4.3 隐式函数
            •        4.4 隐式参数
            •        4.5 隐式类
            •        4.6隐式解析机制
      • 5 泛型
            •        5.1 泛型的基本概念
            •        5.2 协变和逆变

1 .模式匹配基本概念

       1.1 什么是模式匹配
  • 类似于Java中switch语法
       1.2 基本语法
  • 使用match 关键声明,每个分支使用case关键字声明 匹配不成功会执行case _分支
  • 每个 case 中,不需要使用 break 语句,自动中断 case
  • => 后面的代码块,直到下一个 case 语句之前的代码是作为一个整体执行 ,可以
    使用括起来,也可以不括。
object PatternMatching {
  def main (args: Array[String]): Unit = {

        var a: Int = 10
        var b: Int = 20
        var operator: Char = '+'
        var result = operator match {
          case '+' => a + b
          case '-' => a- b
          case '*' => a * b
          case '/' => a / b
          case _ => "matching fail"
        }
    println(result)
  }
}

2.模式匹配–匹配种类

       2.1 匹配常量: 数字 字符 字符串
       2.2 匹配类型: 整形 字符型 集合类型
object PatternType {
  def main (args: Array[String]): Unit = {
    println(decribeConst(true))
    println(decribeType(List(2)))
    println(decribeType(List("hello")))
    println(decribeType(Array(5)))
    println(decribeType(Array("hello")))
  }

  /**
    * 匹配常量
    */
  def decribeConst (x: Any) = x match {
    case 1 => "int one"
    case "hello" => "string hello"
    case true => "boolean true"
    case '+' => "char" + ' '
    case _ => ""
  }

  /**
    * 匹配类型
    */
  def decribeType(x:Any) =x match {
    case i:Int=>"Int"
      // 泛型擦除 List类型编译器会忽略泛型
    case list:List[String]=>"list "
    case array:Array[Int]=>"Array[Int]"
    case a=>"something else"
  }
}

       2.3 匹配具体的集合元素
  • 匹配列表
//  匹配列表
    // 方式一
    for (list <- List(
      List(0),List(1, 0), List(0, 0, 0), List(1, 1, 0),List(88), List("hello")
    )) {
      val result = list match {
        case List(0) => "0"
        case List(x, y) => "List(x, y): " + x + ", " + y
        case List(0, _*) => "List(0, ...)"
        case List(a) => "List(a): " + a
        case _ => "something else"
      }
      println(result)
    }

    // 方式二
    val list1 = List(1, 2, 5, 7, 24)
    val list = List(24)

    list match {
      case first :: second :: rest => println(s"first: $first, second: $second, rest: $rest")
      case _ => println("something else")
    }
    println("===========================")

  • 匹配数组
def main (args: Array[String]): Unit = {
    for (arr <- List(
      Array(0),
      Array(1, 0),
      Array(0, 1, 0),
      Array(1, 1, 0),
      Array(2, 3, 7, 15),
      Array("hello", 1, 30)
    )) {
      val result = arr match {
        case Array(0) => s"0 => ${arr.mkString(",")}"
        case Array(1, 0) => s"Array(1, 0)=> ${arr.mkString(",")}"
        // 匹配两元素数组
        case Array(x, y) => s"Array(x,y)=> ${arr.mkString(",")}"
          //  _* 代表任意多个元素
        case Array(0, _*) => s"以0开头的数组=> ${arr.mkString(",")}"
        case Array(x, 1, z) => s"中间为1的三元素数组=> ${arr.mkString(",")}"
        case _ => s"something else"
      }
      println(result)
    }
  }
  • 匹配元祖
    for (tuple <- List(
      (0, 1),
      (0, 0),
      (0, 1, 0),
      (0, 1, 1),
      (1, 23, 56),
      ("hello", true, 0.5)
    )){
      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 _ => "something else"
      }
   println(result)
       2.4 匹配对象
  • 最原始的 自定义unapply 方法

/**
  * 匹配对象
  */
object MathcObject {
  def main (args: Array[String]): Unit = {

    val student = new Student("alice", 19)
    // 针对对象实例的内容进行匹配
    val result = student match {
      case Student("alice", 18) => "Alice, 18"
      case _ => "Else"
    }
    println(result)
  }

  // 定义类
  class Student (val name: String, val age: Int)
  // 定义伴生对象
  object Student {
    // apply 根据传入的值 构建一个对象
    def apply (name: String, age: Int): Student = new Student(name, age)

    // unapply 根据传入的对象 ,还原属性值 用做模式匹配
    def unapply (student: Student): Option[(String, Int)] = {
      if (student == null) {
        None
      } else {
        Some((student.name, student.age))
      }
    }
  }
}

  • 说明:

    • Student (“alice”, 18) 在执行该语句是 实际调用的是Student1伴生对象中的apply方法
    • 执行 case Student1(“alice”, 18) 时,默认调用unapply方法 Student1作为unapply方法的参数 unapply方法将user对象的name 和age 提取出来 与Student1(“alice”,18) 中的属性值进行匹配
    • case中对象的unapply方法(提取器)返回Some,且所有属性均一致,才算匹配成功,属性不一致,或返回None,则匹配失败。
    • 若只提取对象的一个属性,则提取器为unapply(obj:ob):Option[T]
    • 若提取对象的多个属性,则提取器为unapply(obj:Obj):Option[(T1,T2,T3…)]
    • 若提取对象的可变个属性,则提取器为unapplySeq(obj:Obj):Option[Seq[T]]
  • 使用case关键字简化开发

    • case关键字 默认实现unapply 方法
object Test05_MatchCaseClass {
  def main(args: Array[String]): Unit = {
    val student = Student1("alice", 18)

    // 针对对象实例的内容进行匹配
    val result = student match {
      case Student1("alice", 18) => "Alice, 18"
      case _ => "Else"
    }

    println(result)
  }
}

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

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))

3.模式匹配在变量声明中的使用

object MatchVar {
  def main (args: Array[String]): Unit = {
    val (x,y)=(1,2)
    println(s"$x,$y")//1,2

    val Array(first, second, _*) = Array(1, 7, 2, 9,4)
    println(s"first=$first,second=$second")// first=1,second=7


  }
}

4.隐式转换

       4.1 什么是隐式转化
  • 当编译器第一次编译失败的时候,会在当前的环境中查找能让代码编译通过的方法,用
    于将类型进行转换,实现二次编译
  • 作用: 隐式转换可以在 不需改任何代码的情况下,自动进行类型转换,达到扩展某个类功能的目的
  • 实例: 你是否曾希望某个类有某个方法,而这类的作者却没有提供?举例来说 如果java.io.File 类能有read方法来读取文件 那该多好, val contents =new File(“REACME”).read 作为java程序员 你唯一的出路就是向设计者请愿,给类增加一个这样的方法。 而再scala中你可以定义一个经过丰富的类型,提供你想要的功能
class RichFile(val from:File){
def read=Source.fromFilr(from.getPath).mkString
}

  • 然后再提供一个隐式转换来讲原有的类型转为到这个新类型上

implicit def fire2RichFile(from:File)=new RichFile(from)

  • 这样一来 你就可以在File对象上调用read方法了 它被隐式地转换成一个RichFile。File==>RichFile==>调用RichFile的 read方法
       4.2 什么情况下会进行隐式转化
  • 当对象访问一个不存在的成员时

new File(“README”).read //File 中没有read方法 尝试匹配参数为File对象的隐式函数 尝试将Flie对象转为具有read方法的对象

  • 当表达式类型与预期的类型不同时

sqrt(Fraction(1,4)) // sqrt预期的是一个Double 将调用fraction2Doubel

  • 当对象调用某个方法 而该方法的参数声明与传入的参数不匹配时

3* Fraction(4,5) // 将掉用int2Fraction 因为int的* 方法不接受Fraction作为参数

       4.3 隐式函数
  • 需求:通过隐式转化为 Int 类型增加方法。
package chapter09plus
object Test02_Implicit {
  def main(args: Array[String]): Unit = {
       // 正常调用
    val new12 = new MyRichInt(12)
    println(new12.myMax(15))
    // 1. 隐式函数
    implicit def convert(num: Int): MyRichInt = new MyRichInt(num)
    // 如果整型没有 myMax的方法 编译器会在当前作用域内找implicit声明的函数
    // 尝试将整数转为能调用myMax方法的对象
    println(12.myMax(15))
    println("============================")
    // 2. 隐式类:
    implicit class MyRichInt2(val self: Int) {
      //自定义比较大小的方法
      def myMax2(n: Int): Int = if ( n < self ) self else n
      def myMin2(n: Int): Int = if ( n < self ) n else self
    }
    println(12.myMin2(15))

// 自定义类
class MyRichInt(val self: Int) {
  // 自定义比较大小的方法
  def myMax(n: Int): Int = if ( n < self ) self else n
  def myMin(n: Int): Int = if ( n < self ) n else self
}

       4.4 隐式参数
  • 作用:统一管理多个相同的默认值
  • 说明:普通方法或者函数中的参数可以通过 implicit 关键字声明为隐式参数,调用该方法时,
    就可以传入该参数,编译器会在相应的作用域寻找符合条件的隐式值。
    • 同一个作用域中,相同类型的隐式值只能有一个
    • 编译器按照隐式参数的类型去寻找对应类型的 隐式值,与隐式值的名称无关。
    • 隐式参数优先于默认参数
  • 实例:
// 3. 隐式参数
    implicit val str: String = "alice"
    // implicit val str2: String = "alice2"
    implicit val num: Int = 18
    // 如果不传 在当前作用域内会匹配implicit 定义的同类型的变量,
    def sayHello()(implicit name: String): Unit = {
      println("hello, " + name)
    }
    def sayHi(implicit name: String = "atguigu"): Unit = {// 优先匹配隐式参数
      println("hi, " + name)
    }
    sayHello
    sayHi
    // 简便写法
    def hiAge(): Unit = {
      println("hi, " + implicitly[Int])
    }
    hiAge()
  }
}

       4.5 隐式类
  • 使用implicit关键字修饰的类就是隐式类。若一个变量A没有某些方法或者某些变量时,而这个变量A可以调用某些方法或者某些变量时,可以定义一个隐式类,隐式类中定义这些方法或者变量,隐式类中传入A即可。
  • 隐式类注意:
    • 隐式类必须定义在类,包对象,伴生对象中。
    • 隐式类的构造必须只有一个参数,同一个类,包对象,伴生对象中不能出现同类型构造的隐式类。

//在object当中定义隐式类。使用到前面的person3的参数
  implicit class Person4(p: Person3) {
    def ShowName(): Unit = {
      println(p.name + " is ShowNaame")
    }
  }
  //在mian方法中调用
  val p3 = new Person3("preson3_name")
  p3.ShowName()


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

5 泛型

       5.1 泛型的基本概念
  • 什么是泛型:泛指的类型
       5.2 协变和逆变
  • 语法
class MyList [+T]{ 协变
}
class MyList[-T]{ 逆变
}
class MyList [T]{ 不变
}
  • 说明
    • 协变:Son是 Father的子类 ,则 My List[ Son ] 也作为 MyList[Father] 的子类
    • 逆变:Son是 Father的子类 ,则 My List [Son] 作为 MyList[ Father] 的父类
    • 不变:Son是 Father的子类 ,则 My List [ Son]与 MyList[Father] 无父子关系
TAG

网友评论

共有访客发表了评论
请登录后再发布评论,和谐社会,请文明发言,谢谢合作! 立即登录 注册会员