目录
scala进阶
一、样例类(重点)
1.1、样例类介绍
1、“case class”一般被翻译成样例类,它是一种特殊的类,样例类是不可变的,可以通过值进行比较,可用于模式匹配。
2、样例类的定义方式:
case class 样例类名字(主构造器)
注:主构造器可加可不加,不加的话默认加
1.2、样例类特点
1、自动生成伴生对象,自动生成伴生关系。
2、自动生成的伴生对象object提供apply方法equals,unapply...),进而可以让我们不使用new关键字就能构造出相应的对象,并返回一个class实例。
3、构造器中的每一个参数默认为val,除非显示地声明为var,(也就是说不是自动变成对象私有字段),自动生成类的setter和getter构造器。
1.3、样例类与普通类的区别
1、很显然,样例类有case关键字修饰,而普通类没有
2、样例类的一个优点是方便输出,反而普通类不方便,代码如下:
scala> case class Student(name:String,age:Int) defined class Student scala> println(Student("xiaofeng",18)) Student(xiaofeng,18) scala> class Student1(name:String,age:Int) defined class Student1 scala> println(new Student1("xiaoxiao",18)) $line81.$read$$iw$$iw$Student1@487d4820
1.4、样例类的实战
package com.scala.caseclass /** *样例类特点: * 1、自动生成伴生关系 * 2、自动生成的object对象,提供apply方法(equals,unapply...),并返回一个class实例 * 3、构造器中的每一个参数默认为val,除非显示地声明为var,(也就是说不是自动变成对象私有字段) * 自动生成类的setter和getter构造器 */ case class Student(name:String, age: Int) //特点一: 构造器中的每一个参数默认为val,除非显示地声明为var,(也就是说不是自动变成对象私有字段) object CaseClassDemo { def main(args: Array[String]): Unit = { //创建样例类对象 val stu1 = new Student("jasss",18) // stu1.name="liq" //因为主构造器的参数默认为val 所以无法修改值,并不是对象私有字段 val stu2 = Student("jasss",20) val stu3 = Student.apply("jasss",20) //访问对象值 println(stu2.name,stu2.age,args) //可以直接访问 // stu2.name = "xiaofeng" //修改值就不行 因为是val修饰的变量 相当于java中的常量 println("------------------------") /* 样例类,它是一种特殊的类,样例类是不可变的,可以通过值进行比较 普通类的话,比较的是对象的引用,所以肯定是不同的 */ if (stu1==stu2) println(stu1 + " and " + stu2 + " are the same object") else println(stu1 + " and " + stu2 + " are not the same object") if (stu2==stu3) println(stu2 + " and " + stu3 + " are the same object") } }
1.5、普通类模仿样例类
package com.scala.caseclass
//样例类
case class CaseClassDemo2(name:String,age:Int)
//等价的普通类
class CaseClassDemo1(val name:String,val age:Int) {
override def toString: String = {
(name,age).toString()
}
override def equals(obj: Any): Boolean = super.equals(obj)
//....
}
object CaseClassDemo1 extends App {
def apply(name: String, age: Int): CaseClassDemo1 = new CaseClassDemo1(name, age)
/*
//提取器的第一个功能:完成对象参数的提取
def unapply(arg: User): Option[(String, Int)] = {
if(arg!=null)
Some((arg.name,arg.age))
else
None
}
*/
//提取器的第二个功能,测试一个对象
def unapply(arg: CaseClassDemo1): Boolean= if(arg.age>10) true else false
}
二、模式匹配(重点)
2.1、简单介绍
1、Scala是没有Java中的switch case语法的,但Scala提供了更加强大的match case语法,即模式匹配。
2、
Scala的match case与Java的switch case最大的不同点在于,Java的switch case仅能匹配变量的值,比1、2、3等;
而Scala的match case可以匹配各种情况,比如变量的类型、集合的元素、有值或无值等。
2.2、值匹配
2.2.1、值匹配实战
//提示:其实不论常量匹配还是变量匹配,都是值匹配,自我感觉没啥区别,也没必要特意在意记住什么的
//常量
scala> val char = 'p'
char: Char = p
scala> val ch = char
ch: Char = p
scala> ch match{case 'p'=>println("1111"); case 'y'=>println("222");case _ =>println("333")}
1111
scala> ch match{case 'y'=>println("1111") case 'p'=>println("222") case _ =>println("333")}
222
//总结:其实加不加分号;都是一样的,只要执行了一个case,就不会执行其它的了,反而java中的switch case中需要break进行中断
//变量
scala> import Math.PI
import Math.PI
scala> val ch=Math.PI
ch: Double = 3.141592653589793
scala> ch match {case PI=>println(PI) case _=>println("others")}
3.141592653589793
scala> ch match {case x=>println(ch)}
3.141592653589793
scala> ch match {case y => println("this is "+ch) case PI=>println(PI)}
<console>:15: warning: patterns after a variable pattern cannot match (SLS 8.1.1)
ch match {case y => println("this is "+ch) case PI=>println(PI)}
^
<console>:15: warning: unreachable code due to variable pattern 'y' on line 15
ch match {case y => println("this is "+ch) case PI=>println(PI)}
^
<console>:15: warning: unreachable code
ch match {case y => println("this is "+ch) case PI=>println(PI)}
^
this is 3.141592653589793
scala> val ch = 'a'
ch: Char = a
scala> val y = 'b'
y: Char = b
scala> ch match {case PI=>println(PI); case y => println("this is "+ch)}
this is a
2.2.2、Scala对于常量还是变量模式的规则
1、以小写字母开头的名称为变量模式
2、如果常量符号也是小写字母开头,则需要使用反引号括起来:`i`注意:是反引号` `,而不是单引号‘ ’
2.3、类型匹配
注意: 当你在匹配类型的时候,必须给出一个变量名,否则你将会拿对象本身来进行匹配 。
package com.scala.typeMmatch
import scala.util.Random
object TypeMatchDemo extends App {
//1、
// val arr = Array("this is string",2021,1.0,'a')
// var i= 0
// while(i<10){
// val obj = arr(Random.nextInt(4))
// obj match {
// case a:Int => println(a)
// case b:String => println(b.toUpperCase)
// case _:Double => println(Int.MaxValue)
// case _ =>println("0")
// }
// i+=1
// }
//2、
// class Person
// class Student(val name:String) extends Person
// val stu=new Student("xiaofeng")
//
// println(stu.getClass)
// println(stu.isInstanceOf[Person])
// println(stu.isInstanceOf[Student])
//
// stu match {
// case x:Person=>println("person")
// case x:Student =>println(x.name) //因为有第一个cse,所以成为无法匹配的代码
// }
//3、其实下面两个 都可以
//类型匹配好像不看泛型,此处有疑惑
val lst=List[Any](1,2,3)
lst match {
case x:List[Int]=>println(x)
}
// val lst=List[Int](1,2,3)
//
// lst match {
// case x:List[Any]=>println(x)
// }
}
2.4、集合匹配
2.4.1、字符串匹配
/**
* 模式匹配-字符串
*/
object StringMatchDemo {
def main(args: Array[String]): Unit = {
val arr = Array("zhoudongyu", "yangzi", "guanxiaotong", "zhengshuang")
val name = arr(Random.nextInt(arr.length))
println(name)
name match {
case "zhoudongyu" => println("周冬雨")
case "yangzi" => println("杨紫")
case "guanxiaotong" => println("关晓彤")
case "zhengshuang" => println("郑爽")
case _ => println("Nothing ...")
}
}
}
2.4.2、数组匹配
代码1:
package com.scala.arraymatch object ArrayMatchDemo extends App { val arr1 = Array(0,0,0) // println(Array(0)) println(arr1.isInstanceOf[Array[Int]]) val res = arr1 match { case Array(0) => println(Array(0)(0))// == arr1(0) //匹配1个0的数组 case Array(x, y) => println(s"$x $y") // 匹配任何带有两个元素的数组,并将元素绑定到x和y case Array(0, _*) => println("0...") //匹配任何以0开始的数组 case _ => println("something else") } println(res.getClass) }
代码2:
package com.scala.arraymatch object ArrayMatchDemo1 extends App { val arr = Array(1,2,3,4) arr match { //匹配数组中的前两个元素 // case Array(x,y,_*)=>println(s"$x,$y") //输出 1,2 //匹配数组中的第三个元素 // case Array(_,_,x,_*)=>println(s"$x") //输入3 //匹配第一个元素 case Array(x,tail@_*)=>println(s"$x,$tail") //输出 1,Vector(2, 3, 4) } // 任意字符串@都可以 }
2.4.3、列表匹配
package com.scala.listmatch
object ListMatchDemo extends App {
val list = List(1,2,3,4)
list match {
// case List(x,y,_*)=>println(s"$x,$y") //匹配数组中的前两个元素 输出 1,2
// case List(_,_,x,_*)=>println(s"$x") //匹配数组中的第三个元素 输出3
case List(x,zz@_*) =>println(s"$x,$zz") //输出 1,List(2, 3, 4)
}
}
2.4.4、元组匹配
package com.scala.tuplematch
object TupleMatchDemo extends App {
val tuple = (1,2,3,4)
tuple match {
// case(x,y,_*)=>println(s"$x,$y") // 编译错误,元组不支持_*
case (x,y,z,_) =>println(x,y,z) //输出(1, 2, 3)
case _=>println("others")
}
}
2.5、样例类匹配
总结:
样例类匹配其实与普通类匹配一样的,只不过匹配需要 unapply方法
普通类匹配 需要unapply方法 才可以匹配,也就是说需要我们自己手动重写unapply方法
样例类匹配 则是自动提供了unapply方法,并且已经隐式的调用了
并且什么集合匹配 元组匹配 数组匹配 都隐式的调用了unapply方法
package com.scala.caseclassmatch /** * 样例类匹配其实与普通类匹配一样的,只不过匹配需要 unapply方法 * 普通类匹配 需要unapply方法 才可以匹配,也就是说需要我们自己手动重写unapply方法 * 样例类匹配 则是自动提供了unapply方法,并且已经隐式的调用了 * 并且什么集合匹配 元组匹配 数组匹配 都隐式的调用了unapply方法 * */ object CaseClassMatchDemo extends App{ //1、普通类的模式匹配 val cat1 = new Cat1("xiaofeng",18) //普通类的模式匹配 cat1 match { case Cat1(name,id) => println(name,id) } //2、普通类的模式匹配 val cat = Cat("xiaoxiao",11) cat match { case Cat(name,id) => println(name,id) } } //1、先看一下普通类匹配 class Cat1(val name:String,val id:Int) object Cat1{ def unapply(arg: Cat1): Option[(String, Int)] = { if (arg == null) None //Some(("",0)) else Some((arg.name,arg.id)) } } //2、样例类匹配 case class Cat(val name:String,val id:Int)
2.6、unapply提取器
2.6.1、unapply规则:
1、case 中对象的unapply方法返回Some集合则代表这个对象匹配成功。
2、反之如果返回为none集合则匹配失败。
2.6.2、unapply的实战
package com.scala.unapply import scala.util.Random class UnapplyDemo { } object UnapplyDemo { // apply的构造是返回一个name+数字的字符串 def apply(name:String):String = name+Random.nextLong() def unapply(name:String): Option[String] ={ if(name.nonEmpty) Some(name) else None } def main(args: Array[String]): Unit = { //创建一个对象,此时会触发apply方法 val c = UnapplyDemo("姜小白") println(c) //模式匹配 用来判断UnapplyDemo对象是否创建成功 /* 当然这是自定义的apply 返回的 值,实际上其他的匹配,apply返回是什么不一定 1、当将UnapplyDemo(name)写在case后时 就会默认触发伴生类中unapply方法 name 值会被传递到 def unapply(name:String)方法中就相当于将name赋值给了形参的name 如果判断对象是否创建成功 -->只要返回Some集合就认为创建成功后 开始执行 if判断语句 name.nonEmpty(不是空的) 返回some集合 若为空 返回None case语句后来判断是否创建成功 */ c match { case UnapplyDemo(name)=>println(name+"!!!") case _=>println("没有") } } }
2.7、总结apply和unapply的区别
# apply
- apply方法通常会被称为注入方法(创建对象),在类的伴生对象中做以些初始化的操作
- apply方法的参数列表不需要和原生类的构造方法参数一致
# unapply
- unapply方法通常被称为提取方法,使用unapply方法可以提取固定数量的对象或值
- unapply方法返回的是一个Option类型,因为unapply除了提取值之外,还有一个目的就是判断对象是否创建成功。若成功则有值,反之没有值,所有才使用Option做为返回值类型ps:只要是object类,默认都是提供apply和unapply方法,是被默认隐式调用
2.8、函数匹配(拓展)
package com.scala.functionmatch
/**
* 根据模式匹配写一下函数匹配
*/
object FunctionMatchDemo extends App {
val ff =(x:Int,y:Int)=>x+y //很常见
ff match {
case f:((Int,Int)=> Int)=> println(f(2,8)) //输出10
}
val ff1:(Int,Int)=>Int = (x,y)=>x+y //也挺常见
ff1 match {
case f1:((Int,Int)=> Int)=> println(f1(2,9)) //输出11
}
// val ff2 =(x,y)=>x+y //这么简写不行,在只有一个参数的时候可以这样写(只有一个参数也不能这样写,刚试验了)
val ff2 =(_:Int)+(_:Int)
ff2 match {
case f:((Int,Int)=> Int)=> println(f(2,10)) //输出12
}
}
三、偏函数
3.1、说明
1、只对集合中的一部分数据进行处理的函数就是偏函数(scala.PartialFunction)
2、被包在花括号内没有match的一组case语句是一个偏函数(也就是说case表达式返回偏函数),它是PartialFunction[A, B]的一个实例,A代表输入参数类型,B代表返回类型,常用作输入模式匹配
3、PartialFunction是一个特质(详情请看源码),包括两个方法
a) isDefinedAt()方法: 对哪些数据进行处理
b) apply()方法:如何处理
3.2、偏函数实战
package com.scala.partialfuction /** * 如果使用偏函数对偶数进行处理 */ object PartialFuctionDemo extends App { val pf = new PartialFunction[Int,Double] { override def isDefinedAt(x: Int): Boolean = { x%2 == 0 //返回true 则表示定义对偶数进行处理 } override def apply(v1: Int): Double = { v1.toDouble } } // println(pf(111)) List(1,2,3,4).collect(pf).foreach(println(_)) println(pf.applyOrElse(222,(x:Int)=>0d)) //总结:collect本质上也是调用applyOrElse方法,而applyOrElse则是先调用isDefinedAt 后调用apply // 若调用applyOrElse 返回true 则调用apply // 若调用applyOrElse 返回false 则不调用apply,不进行处理 返回默认的值 //case 表达式返回的也是一个偏函数 List(1,2,3,4).collect({case y:Int if y%2==0 => y.toDouble}).foreach(println(_)) }
collect: 收集一个弹性分布式数据集的所有元素到一个数组中,这样便于我们观察,毕竟分布式数据集比较抽象
被包括在花括号中的case表达式返回的是一个偏函数,实例如下:
scala> val pf = {case x:Int if x%2==0 =>x.toDouble}
<console>:11: error: missing parameter type for expanded function
The argument types of an anonymous function must be fully known. (SLS 8.5)
Expected type was: ?
val pf = {case x:Int if x%2==0 =>x.toDouble}
^
scala> val pf:PartialFunction[Int,Double] = {case x:Int if x%2==0 =>x.toDouble}
pf: PartialFunction[Int,Double] = <function1>