Scala基础 模式匹配样例类与Actor编程

Posted 杀智勇双全杀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala基础 模式匹配样例类与Actor编程相关的知识,希望对你有一定的参考价值。

Scala语法

Scala函数式编程与面向对象编程

模式匹配

之前有写过Scala中也可以模拟出JavaC#switch/case功能。

内容匹配

package com.aa.matchDEMO

import scala.io.StdIn

object MatchCase1 {
  def main(args: Array[String]): Unit = {
    
    //从控制台stdin(标准输入)接收用户输入的内容,根据内容进行模式匹配
    val result: String = StdIn.readLine()       // 死等,这是阻塞方法

    result match {
      case "hadoop" => println("hadoop>>>")
      case "spark" => println("spark>>>")
      case "flink" => println("flink>>>")
      case _=>println("*********hehe*********")
    }
  }
}

多运行几次:

flink
flink>>>
hadoop
hadoop>>>
哈哈
*********hehe*********
spark
spark>>>

Process finished with exit code 0

数据类型匹配

package com.aa.matchDEMO

import scala.util.Random

object MatchCase2 {
  def main(args: Array[String]): Unit = {
    val array = Array(11, 22, "hello", "bye", true, false, 3.14, 6.33)

    val result: Any = array(Random.nextInt(array.length))		//随机抽取数组元素

    result match {
      //case x: Int  => println(x)
      case x: Int if x > 15 => println(x)     				 //匹配时+守卫条件,提前过滤
      case y: String => println(y)
      case z: Double => println(z)
      case w: Boolean => println(w)
      case _ => println("-----呵呵-----")      //不满足上方的任一条件就会落入这里,相当于default
    }
  }
}

随机获取元素并进行类型匹配。使用守卫条件在随机到11时会落入最后的_中。

数组与集合匹配

package com.aa.matchDEMO

import scala.util.Random

//匹配数组、集合
object MatchCase3 {
  def main(args: Array[String]): Unit = {
    var array = Array(1, 3, 5)
    array match {
      case Array(1, x, y) => println(x + " " + y)
      case Array(1, 3, 5) => println("精准匹配")
      case Array(1, _*) => println("1...")
      case _ => println("else")
    }

    //val list = List(0)
    //val list = List(0, "a", 2)
    val list = List(0, "a")
    //val list = List(0, "a")
    //val list = List(1,"a",1)
    list match {
      case 0 :: Nil => println("只有0的列表")
      //case 0::_ =>println("0开头的列表")
      case x :: y :: Nil => println(s"只有2个元素${x},${y}的列表")
      case _ => println("else列表")
    }
  }
}

声明变量时匹配

package com.aa.matchDEMO

//变量声明时进行模式匹配
object MatchCase4 {
  def main(args: Array[String]): Unit = {
    val result: Array[Int] = (1 to 5).toArray

    val Array(_,x,y,_*) = result      //变量声明时进行模式匹配
    println(s"x=${x},y=${y},x+y=${x+y}")  
  }
}
x=2,y=3,x+y=5

Process finished with exit code 0

在声明变量时可以直接进行模式匹配,将匹配的结果作为新声明变量的初值。

样例类

样例类是Scala中的特殊类(case修饰的),用于封装数据,参与模式匹配、传递数据。显然样例类有2种:

  • case class:多例,需要构造器和参数列表。
  • case object:单例,不需要构造器(也就不需要参数列表)。

类+case变成样例类后底层发生了改变:

  • 样例类自动实现了apply方法,不需要new。
  • 样例类自动实现了toString方法,不再是打印内存地址,而是直接输出属性。
  • 样例类自动实现了hashcodeequals方法,对象实例不再是比较内存地址,而是直接比较内容。

举个栗子:

package com.aa.caseDEMO

object Case1 {
  def main(args: Array[String]): Unit = {
    val cat1 = new Cat("哈哈") //普通类需要先new才能创建实例对象
    val cat2 = new Cat("哈哈")

    println(cat1) //输出com.aa.caseDEMO.Cat@4cdbe50f  普通类直接输出的是内存地址
    println(cat1 == cat2) //输出false 直接比较的是内存地址

    val dog1 = new Dog("呵呵") //idea自动提示new是多余的,new关键字发灰说明可以不写
    val dog2 = Dog("呵呵")//不写new也可以创建实例对象
    println(dog1) //Dog(呵呵) 说明自动写好了toString
    println(dog1 == dog2) //true 说明自动写好了equals和hashCode方法
  }
}

class Cat(var name: String) {
  //override def toString: String = super.toString    默认的toString方法
  override def toString: String = s"Cat[$name]"

  //override def equals(obj: Any): Boolean = super.equals(obj)  默认的equals方法
  def canEqual(other: Any): Boolean = other.isInstanceOf[Cat]

  override def equals(that: Any): Boolean = that match {
    case that: Cat => (that canEqual this) && name == that.name //是同一类的实例对象才能比较对象的内容
    case _ => false
  }

  override def hashCode(): Int = { //重写hashCode方法
    //先比较hashCode,如果不同肯定内容不同,如果hashCode相同内容可能不同,再去equals比较内容,速度快
    val state = Seq(name)
    state.map(_.hashCode).foldLeft(0)((a, b) => 31 * a + b) //随便瞎写的算法
  }
}

//样例类
case class Dog(var name: String) {}

样例类的核心功能是封装数据+传输数据+模式匹配

package com.aa.caseDEMO

import scala.util.Random

object Case2 {
  def main(args: Array[String]): Unit = {
    val array1: Array[Object] = Array(new Student("李四"), Teacher("王五"), Teacher("马六"), Zhangsan)
    val array2: Array[Object] = Array(Student("李四"), Teacher("王五"), Teacher("马六"), Zhangsan)

    val result: Object = array2(Random.nextInt(array2.length)) //Object是Java的Object,顶级父类

    result match {
      case Teacher(name) => println(name) //直接提取属性
      case Student(name) => println(name)
      case s: Student => println(s.name) //先匹配对象,在调用对象.属性
      case Zhangsan => println("这货是张三")
      case _ => println("其它情况")
    }
  }
}

class Student(var name: String)

case class Teacher(var name: String)

case object Zhangsan

object Student { //伴生对象内定义apply方法和unapply方法
  def apply(name: String): Student = new Student(name)

  def unapply(s: Student): Option[String] = {
    Some(s.name)
  }
}

补充

apply方法

package com.aa.others

//测试apply方法
object ApplyCase {
  def main(args: Array[String]): Unit = {
    val array1 = new Array[Int](10)       //没有初始值,需要new

    val array2:Array[Int] = Array(11,22,33)    //有初始值,不需要new
  }
}

之前在命令行就发现了创建集合时,有初始值就可以不用new。尝试定位:

在这里插入图片描述

Array.class第27行看到:

  def apply(x : scala.Int, xs : scala.Int*) : scala.Array[scala.Int] = { /* compiled code */ }

Apply其实也是一种构造方法,在创建对象时,如果不+new,编译器就会尝试寻找是否有apply方法,如果有就调用apply方法实现对象的创建。

package com.aa.others

//测试apply方法创建类的实例对象
object ApplyCase1 {
  def main(args: Array[String]): Unit = {
    val student = Student //object是全局唯一的实例(静态加载,单例)

    val zhangsan = new Person("张三")//普通类创建实例对象时必须new
    val lisi:Person = Person("李四")//调用apply方法完成new
  }
}

object Student //object是静态加载的单例

class Person(var name: String)

object Person { //apply方法必须定义在类的伴生对象中
  //idea自动提示并补全apply方法
  def apply(name: String): Person = new Person(name)
}

在类的伴生对象中定义apply方法后,就可以直接调用伴生对象apply方法,不用new。

unapply方法

从字面上也知道先得有apply方法,才能有unapply方法:

object Student{  
def unapply(s: unapplyStudent): Option[String] = {
    Some(s.name)}
}

case Student(name) => println(name)

unapply方法中可以使用Some方法,定位到Some.class

package scala
@scala.SerialVersionUID(value = 1234815782226070388)
final case class Some[+A](val x : A) extends scala.Option[A] with scala.Product with scala.Serializable {
  def isEmpty : scala.Boolean = { /* compiled code */ }
  def get : A = { /* compiled code */ }
}

可以看到Some内部有get方法,其实unapply方法就是从对象中提取属性,相当于提取器。

Option类型

量子叠加类型,表示可有可无的2种状态,分为2个类:有(Some)、无(None)。

package com.aa.others

object OptionCase {
  def main(args: Array[String]): Unit = {
    val map1 = Map("name"->"张三","age"->"13")

    //根据Key去Map中取值,有值显式,无值为None
    val result1:Option[String] = map1.get("name")
    val result2:Option[String] = map1.get("age1")

    println(result1)      //Some(张三)  有值
    println(result2)      //None
  }
}

再看看Option类型多么“薛定谔”:

package com.aa.others

object OptionCase1 {
  def main(args: Array[String]): Unit = {
    def method1(x: Int, y: Int) = x / y

    println(method1(5, 2))
    //println(method1(5, 0))  会报错
    /*
    Exception in thread "main" java.lang.ArithmeticException: / by zero
	at com.aa.others.OptionCase1$.method1$1(OptionCase1.scala:5)
	at com.aa.others.OptionCase1$.main(OptionCase1.scala:6)
	at com.aa.others.OptionCase1.main(OptionCase1.scala)

Process finished with exit code 1
     */

    def method2(x: Int, y: Int): Option[Int] = {
      if (y != 0) Some(x/y)
      else None
    }

    val result = method2(5, 3)
    println(result)
    val maybeInt = method2(5, 0)
    
    maybeInt match {
      case Some(s) =>println(s)
      case None =>println("除数=0,出错")
    }
  }
}

偏函数Partial Function

这个“偏”类似“偏导数”的“偏”。Partial Function字面意思是部分的、不完全的函数。

表面上没有match,实际上却有一组case语句:

偏函数是Partial Function[A,B]的一个实例,A代表输入参数类型,B代表返回结果类型。

举个栗子:

package com.aa.others

object PF1 {
  def main(args: Array[String]): Unit = {
    val array1: Array[Int] = Array(11, 22, 33)
    //println(array1.map(_*10))//[I@48140564  需要转化才能打印
    println(array1.map(_ * 10).toBuffer) //ArrayBuffer(110, 220, 330)

    val array2: Array[Any] = Array(11, 22, "哈哈", 33)
    //println(array2.map(_*10)) 为了保证安全,默认不允许这么做
    println(array2.filter(_.isInstanceOf[Int])//先按数据类型进行合法性过滤
      .map(_.asInstanceOf[Int])//链式编程,对合法数据进行类型转化
      .map(_*10).toBuffer)

    val pf1:PartialFunction[Any,Int]={
      case i:Int=>i*10
    }
    println(array2.collect(pf1).toBuffer)//collect方法接收偏函数作为参数
  }
}

可以看出偏函数相比链式编程的过滤→类型转换→函数运算的三步走,只需要定义输入、输出类型与函数逻辑就可以直接出结果,还是很简洁的。

package com.aa.others

object PF2 {
  def main(args: Array[String]): Unit = {
    def method1(i:Int) = i match {
      case 1 => println("一")
      case 2 => println("二")
      case 3 => println("三")
    }
    method1(2)

    val pf2:PartialFunction[Int,String] ={
      case 1 =>"一"
      case 2 =>"二"
      case 3 =>"三"
    }
    println(pf2(1))
  }
}

使用偏函数进行模式匹配和运算显然也很简洁。

正则

正则(学名Regular Expression),又名规则表达式,用于对字符串内容进行匹配。在文本处理、上位机数据提取、ETL等操作中都需要正则。Scala中的正则类就叫RegEx类,直接new即可使用,当然也可以String.rRegEx类

package com.aa.others

import scala.util.matching.Regex

object RegExTest {
  def main(args: Array[String]): Unit = {
    val email1 = "1009057838@QQ.com"
    val email2 = "haha@com"

    //正常情况使用三对双引号可以跨行
    //正则不行(出现了多余的标记)
//    val regex: Regex =
//    """
//      |.+@.+\\..+
//      |""".r

    /*结果不正确
    List(, , , , , , , , , , , , , , , , , )
    List(, , , , , , , , )
     */

    val regex: Regex =
      """.+@.+\\..+""".r
    println(regex.findAllMatchIn(email1).toList)
    println(regex.findAllMatchIn(email2).toList)
    /*
    List(1009057838@QQ.com)
    List()
     */

  }
}

Scala异常处理

和Java差不多,也可以使用idea的ctrl+art+t来抽取并封装到try/catch/finally中:

package com.aa.others

object ExceptionCase1 {
  def main(args: Array[String]): Unit = {
    try {//idea使用ctrl+art+t抽取并封装在try中
      val error = 5 / 0
      /*常见的分母0报错
       Exception in thread "main" java.lang.ArithmeticException: / by zero
       at com.aa.others.ExceptionCase1$.main(ExceptionCase1.scala:5)
       at com.aa.others.ExceptionCase1.main(ExceptionCase1.scala)
      */
    } catch {//捕获异常后对异常进行处理
      case e1:ArithmeticException => println("数值异常"+e1.getMessage)
      case e2:NullPointerException => println(

以上是关于Scala基础 模式匹配样例类与Actor编程的主要内容,如果未能解决你的问题,请参考以下文章

Scala篇--Scala中Trait模式匹配样例类Actor模型

scala基础语法四(高级)

Scala:基础知识03

Scala:样例类模式匹配Option偏函数泛型

Scala 学习 -- 样例类和模式匹配

Scala 学习 -- 样例类和模式匹配