Scala基础 模式匹配样例类与Actor编程
Posted 杀智勇双全杀
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala基础 模式匹配样例类与Actor编程相关的知识,希望对你有一定的参考价值。
Scala基础(三) 模式匹配、样例类与Actor编程
模式匹配
之前有写过Scala中也可以模拟出Java
和C#
的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方法,不再是打印内存地址,而是直接输出属性。
- 样例类自动实现了hashcode和equals方法,对象实例不再是比较内存地址,而是直接比较内容。
举个栗子:
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.r
→RegEx类
。
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编程的主要内容,如果未能解决你的问题,请参考以下文章