• Scala模式匹配| 隐式转换


     1. 模式匹配

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

    match的细节和注意事项: 

      1)如果所有case都不匹配,那么执行case _ 分支,类似于Java中default语句

      2)如果所有case都不匹配,又没有写case _ 分支,那么会抛出MatchError

      3)每个case中,不用break语句,自动中断case

      4)可以在match中使用其它类型,而不仅仅是字符,可以是表达式

      5)=> 类似于 java swtich 的 :

      6)=> 后面的代码块到下一个case, 是作为一个整体执行,可以使用{} 括起来,也可以不括。 

        for (ch <- "+-3!") {
          var sign = 0
          var digit = 0
          ch match {
            case '+' => sign = 1
            case '-' => sign = -1
            case _ if ch.toString.equals("3") => digit = 3
            case _ => sign = 2
          }
          println(ch + " " + sign + " " + digit)
        }
    
    + 1 0
    - -1 0
    3 0 3
    ! 2 0

    守卫

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

           case _  => digit = 3

           case _  => sign = 2 //不会走这一步,只会走一个case _

    case _ => digit = 3 //默认,如果把它写在最前边,则下面的所有case就都不会走;
    给模式匹配增加变量
    //如果在case关键字后跟变量名,那么match前表达式的值会赋给那个变量
    val ch = 'Y'
    ch match {
    case '+' => println("ok+")
    case a => println("ok2" + a)
    case _ => println ("ok3")
    }
    --->ok2Y
    _在scala中的意思: 导包,隐藏类,初始值,函数返回, 参数占位符,模式匹配其他场合
     val list = List("a", "a", "b", "b" )
    //    list.groupBy(_) 如果写成这个,编译器无法识别是哪个( f(x): 参数还是x: 参数) 省略的
        println(list.groupBy(x => x)) //Map(b -> List(b, b), a -> List(a, a))
    
        val list = List(List(1,2), List(1,2))
        println(list.flatMap(x => x)) //List(1, 2, 1, 2)
    
        val list = List("zs,ls","ww,zl")
        println(list.flatMap(_.split(","))) //List(zs, ls, ww, zl)

    匹配类型

    匹配对象的任意类型,这样做避免了使用isInstanceOf和asInstanceOf方法

        val a = 3
        val obj = if (a == 1) 5  //int...
        else if (a == 2) "2"    //啥也不是
        else if (a == 3) BigInt(3)  //2147483647
        else if (a == 4) Map("aa" -> 1) //对象是一个字符串-数字的Map集合
        else if (a == 5) Map(1 -> "aa") //对象是一个字符串-数字的Map集合
        else if (a == 6) Array(1, 2, 3) //对象是一个数字数组
        else if (a == 7) Array("aa", 1) //啥也不是
        else if (a == 8) Array("aa") //对象是一个字符串数组
    
        obj match {
          case a: Int => println("int...")
          //case _ : BigInt => Int.MaxValue
          case b: Map[String, Int] => println("对象是一个字符串-数字的Map集合")
          case c : Map[Int, String] => println("对象是一个数字-字符串的Map集合")
          case d : Array[String] => println("对象是一个字符串数组")
          case e : Array[Int] => println("对象是一个数字数组")
          case _ : BigInt => println(Int.MaxValue)// <=等效于=> case f : BigInt => println(Int.MaxValue)
           // 这里case _ 表示隐藏变量名,即不使用,而不是表示默认匹配,忽略匹配的变量值。
          case _ => println("啥也不是")
        }

    匹配规则

       for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0), Array(1, 1, 0, 1))){
          val result = arr match {
            case Array(0) => "0"  //Array(0) 匹配只有一个元素且为0的数组
            case Array(x, y) => x + "=" + y //) 匹配数组有两个元素,并将两个元素赋值为x和y
            case Array(0, _*) => "以0开头和数组" //Array(0,_*) 匹配数组以0开始
            case _ => "什么集合都不是"
          }
          println("result=" + result)
        }
    /*    result=0
        result=1=0
        result=以0开头和数组
        result=什么集合都不是
        result=什么集合都不是*/

    匹配对象

        // TODO 匹配对象
    object Square {
      // unapply可以理解为apply方法的反向操作
      // unapply一般使用在模式匹配中
      def unapply(z: Double): Option[Double] = Some(math.sqrt(z))
      def apply(z: Double): Double = z * z //apply方法用于生成伴生类对象,其实也可以理解对象的工厂
    }
        // 模式匹配使用:
        val number: Double = 36.0
        number match {
          case Square(n) => println(n) //6.0
          case _ => println("nothing matched")
        }

     匹配for循环

      val map = Map("a"->1, "b"->0, "c"->3)
        for ((k, v) <- map){
          println(k + "->" + v)
        }
        for ((k, 0) <- map) {
          println(k + " --> " + 0) //b --> 0
        }
        for ((k, v) <- map if v == 3){
          println(k + " --> " + 3) //c --> 3
        }

    样例类  可直接构建对象且可以做模式匹配

    样例类仍然是类; 样例类用case关键字进行声明。 样例类是为模式匹配(对象)而优化的类

    构造器中的每一个参数都成为val——除非它被显式地声明为var; 

    在样例类中一定会有伴生对象,它提供apply方法让你不用new关键字就能构造出相应的对象;  提供unapply方法(与apply是反向的,由对象可推断出类型)让模式匹配可以工作

    将自动生成toString、equals、hashCode和copy方法(有点类似模板类,直接给生成,供程序员使用)

    除上述外,样例类和其他类完全一样。你可以添加方法和字段,扩展它们

        abstract class Amount
        case class Dollar(value: Double) extends Amount //继承抽象类
        case class Currency(value: Double, unit: String) extends Amount //样例类不需要{ }里边的主体内容了,case 类名就相当于伴生对象,伴生对象就是负责来构建对象;
        case object NoAmount extends Amount
    
        //说明: 这里的 Dollar,Currencry, NoAmount  是样例类。
        //可以这样理解样例类,就是样例类会默认其它很多的方法,供程序员直接使用
        //当我们有一个类型为Amount的对象时,可以用模式匹配来匹配他的类型,并将属性值绑定到变量(即:把样例类对象的属性值提取到某个变量)
        for (amt <- Array(Dollar(1000.0), Currency(1000.0, "RMB"), NoAmount)) { //Araay里边有3个对象,它们不是new出来的,伴生对象不需new可直接传值(里边有apply方法,不需声明)
          val result = amt match {  //进行匹配
            case Dollar(v) => "$" + v
            case Currency(v, u) => v + " " + u
            case NoAmount => ""
          }
          println(amt + ": " + result)
    /*      Dollar(1000.0): $1000.0
            Currency(1000.0,RMB): 1000.0 RMB
            NoAmount: */
        }
        case class User(var name: String)  //把参数变成了属性,加上var即是可变的
        val user: User = User("kris")
        //放不进去,要在name参数前加var
        user.name = "alex"
        println(user.name) //alex

    密封类

    把所有的样例类都放一块,加上sealed后,就只能放在一块了,不能放在别的源文件中;

    如果想让case类的所有子类都必须在申明该类的相同的源文件中定义,可以将样例类的通用超类声明为sealed,这个超类称之为密封类。
    密封就是不能在其他文件中定义子类。

    2. 隐式转换

    编译器是看编译语法,不会给你执行;

    隐式转换即二次编译(自动转换) 不改变之前程序前提下,扩展功能

    object ObjectTest1 extends App {
      //自动转换
      val parent: ParentClass = new ChildClass  //子类对象可自动转换为父类 
      val child: ChildClass = parent.asInstanceOf[ChildClass]  //父类对象不能直接转换为子类 ,要先判断下是不是这个类的实例
      // byte => short ==> int ==> long
      var b : Byte = 19
      var s : Short = b
      b = s.toByte
    
    }
    class ParentClass{
    }
    class ChildClass extends ParentClass{
    
    }

    scala允许开发者自己定义类型转换规则,遵循了OCP规则(不对之前的代码做任何改变,隐式转换即可实现)

    当scala编译器在编译时发现类型错误,尝试从开发者的位置查找转换规则进行二次编译

    object Testimplicit extends App {
    
      implicit def tranform(d: Double): Int = {
        d.toInt
      }
      val i : Int = 3.4
      println("i = " + i)
    }

    隐式转换规则不仅仅是可以转换类型,还可以扩展功能

    object Testimplicit extends App {
    
      class Person {//extends User
    
      }
      class User{
        def login()={
          println("user login....")
        }
      } //隐式转换可以将指定的类型转换为其他的类型,进行功能的扩展
      implicit def transform(p:Person): User ={ //想要一个Person,传参; User类型的
        new User
      }
      var p = new Person
      p.login  //把用户的功能给扩展过来,可以继承(从概念上反了应该User继承Person),这时可用隐式转换
    }
    
    ==>user login....

    scala中允许方法参数进行隐式转换  相同类型的参数做转换;

      def test(implicit name: String = "kris")={  //方法里边这个值是写死的
        println(name)
      }
      //scala中允许方法参数进行隐式转换
      implicit val username: String = "alex"  //这个值是可以灵活变化的,可以把一个动态变化的值传进去,而不是一个固定值 
      test //不传参数结果是alex,scala在编译时就会尝试着在作用域内看看有没有一个隐式的变量可以用,如果有就把这个隐式的变量传进来;
      test() //kris

     隐式转换类是从scala2.10版本后增加的,类声明时必须有一个参数的构造方法;

      通过一个方法把一个类型变成另外一个类型,隐式类是把一个类型转换为当前类型 

        implicit class Person1(user: User11){
          def test()={
            println("test...")
          }
        }
        class User11{
    
        }
        val user = new User11
        user.test()  //test...

    隐式转换在scala编译时进行查找,默认当前作用域范围;  如果当前作用域范围查找不到,会从上级的伴生对象, 包对象查找

    object Testimplicit extends App {
      val users = new Users()
      users.test()
      class Users extends MyTrait{
    
      }
    }
    
    trait MyTrait{
    }
    object MyTrait{
      implicit class Test1(name: Users){
        def test()={
          println("test...")
        }
      }
    }
  • 相关阅读:
    检索 COM 类工厂中 CLSID 为 {00024500-0000-0000-C000-000000000046} 的组件失败,原因是出现以下错误: 80070005 拒绝访问。 (异常来自 HRESULT:0x80070005 (E_ACCESSDENIED))。
    SAP middb主键加索引
    【深入理解JVM】类加载器与双亲委派模型 (转)
    一千行MySQL学习笔记 (转)
    Spring框架是怎么解决Bean之间的循环依赖的 (转)
    一步一步带你入门MySQL中的索引和锁 (转)
    如何决定使用 HashMap 还是 TreeMap? (转)
    JVM 线上故障排查基本操作 (转)
    分布式、集群、微服务的区别
    可能是把 Java 内存区域讲的最清楚的一篇文章
  • 原文地址:https://www.cnblogs.com/shengyang17/p/10645381.html
Copyright © 2020-2023  润新知