Scala编程(三)高级特性
模式匹配
Scala有一个十分强大功能:模式匹配。类似于java中的switch case 语法,即对一个值进行条件判断,然后针对不同的条件,进行不同的处理。
另外Scala还提供了样例类,对模式匹配进行了优化,可以快速进行匹配。
1.匹配字符串
object matchDemo1 extends App {
//定义一个字符串数组
val course = Array("hadoop","spark","scala","java")
val name = course(3)
//字符串匹配
name match {
case "hadoop" => println("分布式存储和计算框架")
case "spark" => println("分布式内存计算框架")
case "scala" => println("多范式编程语言")
//可以添加守卫条件
case _ if (name.equals("java")) => println("面向对象的编程语言")
case _ => println("不认识。。")
}
}
2.匹配类型
object matchDemo2 extends App {
//定义一个数组
val arr = Array("hadoop",10,100.0,true,matchDemo2)
val name = arr(4)
//类型匹配
name match {
case x : String => println("字符串类型:"+x)
case y : Int => println("整数类型:"+y)
case z : Double => println("小数类型:"+z)
case b : Boolean => println("布尔类型:"+b)
case a : matchDemo2.type => println("matchDemo2类型:"+a)
case _ => println("不匹配的类型")
}
}
3.匹配数组、元组、集合
3.1匹配数组
object matchDemo3 {
def main(args: Array[String]): Unit = {
println("参数值为:" + args.toBuffer)
//args可以在Edit configurations中修改
args match {
case Array(x,y,z) => println("参数有三个")
case Array(x) => println("参数有一个")
case Array(x,"world",y) => println("参数中间为world")
case _ => println("不能识别")
}
}
}
3.2匹配元组
object matchDemo4 {
def main(args: Array[String]): Unit = {
var tuple = (10,20,30)
//匹配元组
tuple match {
case (1,x,y) => println("数据为" + 1,x,y)
case (_,x,30) => println("数据为" + x)
case _ => println("其他。。。。")
}
}
}
样例类
样例类和普通类的区别:
都可以实例化对象
样例类: 可以实例化对象、可以不使用new就可以实例化对象,自动生成apply()和unapply()方法
样例类就是因为生成了unapply() 才可以被 match匹配
case class Student(参数列表) //可以指定构造中的参数
case object Student //只能是无参
match中只能匹配样例类
class Person{}
case class Student(name: String) extends Person {}
case object Teacher extends Person {}
object matchDemo5 extends App {
val stu = new Student("李四")
val tea = Teacher
classroom(stu)
classroom(tea)
def classroom(p : Person): Unit ={
p match {
case Student(name) => println(name + "是学生")
case Teacher => println("是老师")
case _ => println("不能识别")
}
}
}
option类型
在Scala中Option类型用样例类来表示可能存在或者可能不存在的值(Option的子类有Some和None)。Some包装了某个值,None表示没有值
object matchDemo6 extends App {
var map = Map(("李四",20),("王五",30),("张三",40))
//通过Key获取value
var value = map.get("李四") match {
case Some(x) => x
case None => 0
}
//简写:
val v = map.getOrElse("一一",0)
println(value)
println(v)
}
函数式编程
函数式编程体现
1.将函数赋值给变量
在Scala语言中,允许把函数赋值给一个变量。
语法格式:val 变量 = 函数名 空格 下划线
val func = function _ //注意函数名不需要书写:()
object FunctionDemo1 extends App {
var hello = sayHello _
hello("jack")
def sayHello(name:String): Unit ={
println("hello,"+ name)
}
}
2.匿名函数
object FunctionDemo1 extends App {
var hello = (name :String) => println("hello,"+ name)
hello("jack")
}
3.高阶函数
高阶函数:接收其他函数作为参数的函数,在scala中称为高阶函数。
3.1将函数作为参数传递
object FunctionDemo2 extends App {
//将值赋给一个变量
var hello =GoodMorning _
sayHello(hello,"张三")
sayHello(GoodNight,"李四")
//早上好
def GoodMorning(name :String): Unit ={
println("早上好,"+ name)
}
//晚上好
def GoodNight(name :String): Unit ={
println("晚上好,"+ name)
}
//定义高阶函数
//f: 对象类型 (String) => Unit 函数类型对象
def sayHello(f : (String) => Unit,name:String): Unit ={
f(name)
}
}
3.2将函数作为返回值
object FunctionDemo3 extends App {
// val sayHello = GoodMorning _
// sayHello("早上好")("李四")
val sayHello = GoodMorning("早上好")
sayHello("李四")
def GoodMorning(hello : String) ={//自动推断返回值类型
//使用匿名函数来作为返回值
(name :String) => println(hello + "," + name)
}
}
4.闭包
闭包: 函数内部的变量不在其有效的作用域时,仍然可以对这个变量进行访问。
object FunctionDemo3 extends App {
val sayHello = GoodMorning("早上好")
sayHello("李四")
def GoodMorning(hello : String) ={//自动推断返回值类型
//使用匿名函数来作为返回值
//函数中使用了GoodMorning中的局部变量hello
(name :String) => println(hello + "," + name)
}
}
闭包的原理:在Scala中函数是做为对象存在的
给每个函数,创建一个函数对象,在函数对象中,把函数的参数作为它的一个实例变量,这样就可以让函数的参数脱离它之前的局域变量作用域,那么其它函数在使用时也是通过实例.变量去访问
5.常用的高阶函数
1.map函数:对传入的每个元素进行映射,返回处理后的元素
object FunctionDemo4 extends App {
val arr = Array(1,2,3,4)
//对每个元素加10
val result = arr.map(_+10)
println(result.toBuffer)
}
2.foreach函数: 对传入的每个元素都进行处理,但是没有返回值
object FunctionDemo5 extends App {
val result = (1 to 4).map(_+10)
result.foreach(x =>
if (x % 2 == 0)
println(x)
)
}
3.filter函数: 对传入的每个元素都进行条件判断,条件为true,则保留该元素,否则过滤掉该元素
object FunctionDemo6 extends App {
val arr = Array(1,2,3,4,5,6,7,8,9)
val result = arr.filter(x =>
if (x % 2 ==0)
true
else false
)
println(result.toBuffer)
}
4.reduceLeft函数:从左边元素开始,进行reduce操作,即先对元素1和元素2进行处理,然后将结果与元素3处理,再将结果与元4处理,依次类推
object FunctionDemo7 extends App {
val arr = Array(1,2,3,4,5,6,7,8,9)
val result1 = arr.reduceLeft(_ * _)
println("阶乘为:" + result1)
val result2 = arr.reduceLeft(_ min _)
println("最小值为:" + result2)
}
5.flatMap函数:将多行子句拆分成单词
object FunctionDemo8 extends App {
val arr = List("hello world scala","hadoop spark")
val result = arr.flatMap(_.split(" "))
println(result)
}
6.zip函数:拉链操作,将多个值进行关联
其中一个元素的个数比较少,可以使用zipAll用默认的元素填充
例:List(”小明”,”小红”,”小强”).zipAll(List(90,55) ,“默认”, 0)
object FunctionDemo9 extends App {
val arr1 = List("李四","王五","张三")
val arr2 = List(13,14,15)
val arr3 = List(13,14)
//两个集合元素一一关联
val result1 = arr1.zip(arr2)
println(result1)
//元素数量不匹配,指定默认值
val result2 = arr1.zipAll(arr3,"默认",0)
println(result2)
}
案例实现
统计各字符串出现的次数
object FunctionDemo10 extends App {
val arr = Array("hello world java hi","hi hello world")
val result = arr.flatMap(_.split(" "))
.groupBy(x => x)
.map(el => (el._1,el._2.length))
println(result)
}
隐式转换和隐式参数
Scala语言提供了一种非常有特色的功能:隐式转换和隐式参数
隐式转换(利用隐式函数实现),简单的来讲就是把某种类型对象转换成其他类型的对象
隐式参数,类似于Spring中的依赖注入。在调用函数时,一般函数的参数应该是手动的去指定,而当定义了隐式参数后,scala会去上下文中通过参数的类型去找,并把找到的参数注入到当前函数中
注意:隐式参数和隐式函数必须书写在object中
1.隐式转换
隐式转换:将某种类型的对象转换成其他类型的对象或者是给某个类增加方法,从而来增强功能或增加一些特殊功能。
在Scala中定义的隐式转换函数,只要在当前程序的作用域内或者手动导入隐式转换函数,就会被Scala自动使用所定义隐式转换函数,从而进行类型的转换
隐式转换函数和普通函数在区别是,隐式转换函数要使用implicit开头,并且一定要定义函数的返回值类型
特殊人群买票案例
case class Student(name:String)
case class Soldiery(name:String)
case class SpecialPerson(name :String)
object MyImplicit{
//隐式转换函数和隐式参数只能书写在object中
implicit def person2specialPerson(obj : Object) :SpecialPerson = {
obj match {
case Student(name) => new SpecialPerson(name)
case Soldiery(name) => new SpecialPerson(name)
}
}
}
object implicitDemo1 extends App {
//手动导入隐式转换函数
import MyImplicit.person2specialPerson
val stu = new Student("张三")
buyTicket(stu)
val soldiery = new Soldiery("李四")
buyTicket(soldiery)
def buyTicket(p : SpecialPerson): Unit ={
println(p.name +"可以买半价票")
}
}
使用隐式转换加强类型
案例:超人变身
class Man{
def work: Unit ={
println("努力工作")
}
}
class SuperMan{
def fly: Unit ={
println("飞起来了..")
}
}
object implicitDemo2 extends App {
implicit def man2superMan(man :Man) = new SuperMan
val man = new Man()
man.work
man.fly
}
隐式转换发生的时机:
在书写了隐式转换函数后,通常有2种情况会自动发生隐式转换:
1、当使用对象调用类中不存在的方法或成员时,编译器会自动将对象进行隐式转换(超人变身案例)
2、方法中所定义的接收参数类型与调用方法时传递的参数类型不匹配时(特殊人群买票案例)
2.隐式参数
所谓的隐式参数,指的是在函数或者方法中,定义一个用implicit修饰的参数,此时Scala会尝试找到一个指定类型的,用implicit修饰的参数,即隐式值,并注入参数。
Scala会在两个范围内查找:
1、当前作用域内可见的val或var定义的隐式值
2、隐式参数类型的伴生对象内的隐式值
案例:员工领工资
object company{
//隐式值写在object中,不能重复出现
implicit var name :String = "小明"
implicit var money :Double = 2000.0
}
class Boss{
def getName()(implicit name : String)= "名字:" + name
def getMoney()(implicit money : Double) = "薪资:" + money
}
object implicitDemo3 extends App {
//导入隐式值
import company._
var boss = new Boss()
println(boss.getName()+"----"+ boss.getMoney())
}
案例:明星签名
class Pen{
def write(content :String): Unit ={
println(content)
}
}
object Pen{
//隐式参数写在参数类型所在的伴生对象中
implicit val p : Pen = new Pen()
}
object implicitDemo4 extends App {
qianming("李四")
def qianming(name :String)(implicit p : Pen): Unit ={
p.write(name + "签名")
}
}
类型参数
在Scala中,可以在类,集合,函数中定义参数类型,从而保证使用到该参数的地方只能使用这种类型,保证程序的健壮性
1.泛型类
泛型类:在Scala的类中定义参数类型
应用场景:在需要对类中的成员(变量和方法)进行统一的参数类型限制
class demo1[T]{
//可变集合
private var l = ListBuffer[T]()
//添加
def add(el : T): Unit ={
l.append(el)
}
}
object demo1 extends App {
val l = new demo1[Int]
l.add(20)
//l.add("String")这里不能添加除int以外其他类型的参数
}
2.泛型函数
泛型函数:类似于泛型类,在声明函数时指定泛型类型,在函数体中就能使用这个类型来声明变量或者返回值类型
案例:获取编号
object demo2 extends App {
val num = matchNum[Int](222)
println(num)
def matchNum[T](n :T)={
n match {
case n :String =>"231321412"
case n :Int =>123123123
case _ => "无法识别"
}
}
}
3.上下界
在java中:
?extends T : ?必须满足是T类型的子类或者是T类型
?super T : ? 必须满足是T类型或者是T类型的父类
在Scala中:
上边界:上边界定义语法:T <: 父类
下边界:下边界定义语法:T >: 子类
上边界案例:同班
class classroom{
var name :String = _
def same(c :classroom)={
println(this.name + "和" + c.name + "在一个班")
}
}
class Student1(stu1name :String) extends classroom{
this.name = stu1name
}
class Student2(stu2name :String) extends classroom{
this.name = stu2name
}
class room[T <: classroom](c1 :T,c2 :T){
def sameroom = c1.same(c2)
}
object demo3 extends App {
val stu1 = new Student1("张三")
val stu2 = new Student2("李四")
val room = new room(stu1,stu2)
room.sameroom
}
下边界案例:获取物品
class Father(name :String){}
class Child(name :String) extends Father(name : String){}
class xiaoming(name :String)
object demo4 extends App {
val father = new Father("张三")
val child = new Child("张小")
val xiaoming = new xiaoming("小明")
getgoods(xiaoming)
def getgoods[T >: Child](person : T): Unit ={
if (person.getClass == classOf[Child]){
println("儿子领取。。。。")
}else if(person.getClass == classOf[Father]){
println("父亲领取。。。。")
}else{
println("不能领取")
}
}
}
4.视图界定(View Bounds)
在Scala中还提供了一种上下边界的加强版:视图界定
视图界定语法: <%
<% 它比 <: 适用的范围更广,除了所有的子类型, 还允许隐式转换过去的类型
5.协变和逆变
Scala中提供了协变(+)和逆变(-)来解决参数化类型的泛化问题
5.1协变
[+类型]:协变。 把子类型转换为父类类型 (从下往上:类型上升)
案例:大师与专家级别都有入场资格
class Master{}
class Professional extends Master{}
class Card[+T](n : String){} //协变 子类型转换为父类型
object demo5 extends App {
var card = new Card[Professional]("李四")
enter(card)
def enter(card :Card[Master]): Unit ={
println("大师级别")
}
}
案例:只允许专家级别进入
class Master{}
class Professional extends Master{}
class Card[+T](n : String){} //协变 子类型转换为父类型
object demo5 extends App {
var card = new Card[Master]("李四")
enter(card) //报错 协变不能将mater类型转为professional类型
def enter(card :Card[Professional]): Unit ={
println("只允许专家级别进去")
}
}
5.2逆变
[-类型]:逆变。 把父类类型强制转换为子类类型 (从上往下:类型下降)
案例:进入的要求最低是专家级别
class Master{}
class Professional extends Master{}
class Card[-T](n : String){} //逆变 父类型转换为子类型
object demo5 extends App {
var card = new Card[Master]("李四")
enter(card)
def enter(card :Card[Professional]): Unit ={
println("专家级别以上进去")
}
}
案例:进入最低要求是大师级别
class Master{}
class Professional extends Master{}
class Card[-T](n : String){} //逆变 父类型转换为子类型
object demo5 extends App {
var card = new Card[Professional]("李四")
enter(card) //报错 逆变不能将professional类型转换成master类型
def enter(card :Card[Master]): Unit ={
println("大师级别会场")
}
}