1、变量与数据类型
1、注释
1、单行注释: //
2、多行注释: / .. /
3、文档注释: /* ../
2、标识符的命名规范
scala中标识符必须是数字、字母、下划线、$、特殊符号,首字母不能是数字
scala特殊符号一般是scala内部使用
实际工作中,标识符命名依然采用驼峰原则
3、变量
1、语法: val/var 变量名:类型 = 值
2、val与var的区别
val修饰的变量类似java final修饰的变量,不能修改值
var修饰的变量可以修改值
3、定义变量的时候,变量类型可以省略,省略之后scala会自动推断
4、定义变量的时候必须初始化
5、scala中一行如果只写一个语句,分号可以省略。一行多个语句的时候分号不能省略
4、字符串
1、双引号包裹: val name = “lisi”
2、通过new创建: val name = new String(“liis”)
3、拼接
1、通过+拼接: “aa”+”bb”
2、插值表达式: s”${外部变量名/值/表达式}”
4、一些方法: toString,subString,..
format[将字符串中的占位符用指定的值替换]方法: 字符串.format(值,…)
%s: 字符串的占位符
%d: 整数的占位符
%f: 浮点型的占位符
“%s:xxx”.format(“yyy”)
5、三引号: “””…”””
三引号能够完整保留字符串的输入格式,三引号一般在工作中用于sql语句
5、键盘输入
1、从控制台读取数据: StdIn.readLine/readInt/readBoolean/..
2、从文件读取数据: Source.fromFile(文件路径,文件编码格式).getLines
6、数据类型
Any: 所有类的父类
AnyVal: 值类型
Byte、Short、Int、Float、Double、Char、Boolean、Long
Unit: 相当于java的void, 有一个实例()
Stringops: 对java字符串的扩充
AnyRef: 引用类型
String,集合,数组,java class,scala class
Null: 所有引用类型的子类,有一个实例null,一般用于给引用类型赋予初始值,在给引用类型赋予初始值的时候必须指定变量类型
Noting: 所有类型的子类,scala内部使用
7、数值与字符串的转换
1、数值与数值的转换
1、低精度转高精度[Int->Long]: 自动转换
2、搞定度转低精度[Long->Int]: toXXX方法
2、数值与字符串的转换
1、数值转字符串
1、toString方法
2、拼接: 通过+拼接/通过插值表达式
2、字符串转数值: toXXX方法
2、运算符
scala中没有++、—、三元运算符。其他运算符与java一样,用法也一样
scala中的运算符是一个个的方法。
scala中方法调用的两种方式:
1、对象.方法名(参数值,…)
2、对象 方法名 (参数值,…) [如果方法只有一个参数,()可以省略]
3、流程控制
1、块表达式:
1、定义: 由{}包裹的一段代码称之为块表达式
2、块表达式有返回值,返回值是{}中最后一个表达式的结果值
2、分支控制
1、单分支: if(布尔表达式){…}
2、多分支: if(布尔表达式){..} else if(布尔表达式){..}.. else{..}
3、双分支: if(布尔表达式){..} else{…}
scala分支控制用法与java分支控制一样
scala的分支控制有返回值,返回值就是符合条件的分支的块表达式的结果值
3、for循环
for循环两个重要方法:
to:
语法: start.to(end) / start to end
结果: 生成一个左右闭合的集合
until:
语法: start.until(end) / start until end
结果: 生成一个左闭右开的集合
1、语法: for( 变量名 <- 集合/数组/表达式){…}
2、守卫: for( 变量名 <- 集合/数组/表达式 if(布尔表达式)){…} [每次循环的时候必须先满足if判断的条件才能执行循环体]
3、步长: for( 变量名 <- start to/until end by step){…}
4、嵌套for循环: for( 变量名1 <- 集合/数组/表达式;变量名2 <- 集合/数组/表达式){…}
5、引入变量: for( 变量名1 <- 集合/数组/表达式;变量3=值;变量名2 <- start to 变量3){…}
6、yield表达式:
for循环的循环体{}默认不能当做块表达式来看,如果for默认没有返回值
如果想要for有返回值,可以使用yield表达式
语法: for( 变量名 <- 集合/数组/表达式) yield{…}
4、while\do-while循环
scala while\do-while循环与java完全一样
5、break与continue
scala中没有break与continue关键字
scala实现break与continue功能:
实现break:
1、导入包: import scala.util.control.Breaks.
2、通过breakable与break方法实现
breakable({
while(循环条件){
if(..) break()
…
}
})
实现continue:
1、导入包: import scala.util.control.Breaks.
2、通过breakable与break方法实现
while(循环条件){
breakable({
if(..) break()
…
})
}
5、函数式编程
方法就是函数,函数也是对象
1、方法
1、语法: def 方法名(参数名:参数类型,…):返回值类型 = {方法体}
def add(x:Int,y:Int):Int= {x+y}
2、方法的简化
1、如果方法体只有一行语句,此时方法体的{}可以省略
def add(x:Int,y:Int):Int= x+y
2、如果将方法体块表达式的结果值作为方法的返回值,此时定义方法的时候方法的返回值类型可以省略
如果方法体中有return关键字不能省略方法的返回值类型
def add(x:Int,y:Int)= x+y
3、如果方法不需要返回值的时候,=可以省略[=与{}不能同时省略]
def hello():Unit = {println(“..”)}
简化:
def hello(){println(“..”)}
4、如果方法不需要参数的时候,()可以省略
def hello{println(“..”)}
1、如果定义方法的时候没有(),在调用方法的时候()不能带上
2、如果定义方法的时候有(),在调用方法的时候()可有可无
3、方法参数
1、默认值参数: 调用方法的时候有默认值的参数可以不用传值
语法: def 方法名(参数名:参数类型=默认值,…):返回值类型 = {方法体}
有默认值的参数最好放在参数列表最后面
2、带名参数: 调用方法的时候指定将值传递给哪个参数
def add(x:Int=10,y:Int) = x+y
调用:
add(y=20)
3、可变参数:
语法: def 方法名(参数名:类型,..,参数名:参数类型):返回值类型 = {方法体}
注意: 可变参数必须放在参数列表最后面
java中可变参数可以直接传递数组,scala可变参数不能直接将数组传入,如果想要将数组/集合的所有元素传递给可变参数,应该通过 集合名:_ 的方式传递
2、函数
1、语法: val 函数名 = (参数名:类型,..) => {函数体}
函数是一个对象
2、函数的简化: 如果函数体只有一行语句,此时函数体的{}可以省略
3、方法和函数的区别
区别:
1、方法是存储在方法区中,函数是对象是存在堆中
2、方法如果定义在object/class中可以重载的,函数是对象,函数名就是变量名,函数不能重载[同一个作用域不能有同名参数]
联系:
1、方法可以手动转成函数: 方法名
2、方法如果定义在方法中,此时就是函数
4、高阶函数
1、定义: 使用函数作为参数或者是返回值的方法/函数称之为高阶函数
def add(x:Int,y:Int,func:(Int,Int)=>Int):Int = { func(x,y) }
2、调用高阶函数的简化
val func = (x:Int,y:Int) => x+y
add(10,20,func)
1、直接传递函数的值: add(10,20,(x:Int,y:Int) => x+y)
2、函数的参数类型可以省略: add(10,20,(x,y) => x+y)
3、如果函数的参数在函数体中只使用了一次,可以用代替: add(10,20,+) [第N个下划线代表函数第N个参数]
注意: 以下三种情况不能用代替
1、函数的参数的定义顺序与在函数体中使用顺序不一致的时候,不能用代替:
add(10,20,(x,y) => y-x)
不能简化为:
add(10,20,-)
2、如果函数体中存在嵌套[()],此时函数的参数在嵌套中以表达式的形式存在的时候,不能用代替
add(10,20,(x,y) => (x+10)*y)
不能简化为:
add(10,20,(+10))
3、如果函数参数个数只有一个,并且在函数体中直接将参数返回,此时不能用代替
def m1(x:Int,func: (Int) =>Int ) = func(x)
m1(10,x=>x)
不能简化为:
m1(10,)
4、如果函数只有一个参数,那么定义函数的时候()可以省略
m1(10,(x)=>x) 简化为: m1(10,x=>x)
5、匿名函数
定义: 没有函数名的函数称之为匿名函数
匿名函数不能单独调用,只能作为高阶函数的参数进行传递
6、函数柯里化&闭包
1、柯里化:
1、定义: 有多个参数列表的方法称之为柯里化
def add(x:Int)(y:Int) = x+y
2、闭包
1、定义: 函数体中调用了外部变量的函数称之为闭包
7、递归
1、定义: 自己调用自己称之为递归
递归方法必须满足两个条件:
1、必须要有退出条件,不能陷入死循环
2、必须定义返回值类型
8、控制抽象
1、语法: 空格=>返回值类型
2、定义: 控制抽象不能单独使用,只能作为方法的参数存在,代表的就是一个块表达式,后续控制抽象可以当做函数一样调用[调用的时候不能有()]
def add(func: =>Int) = func
add({
..
10
})
9、惰性求值
1、语法: lazy val 参数名:类型 = 值
通过lazy表示的参数不会立即初始化,等到真正使用的时候才会初始化
5、面向对象
1、包
包的使用
1、声明包: 在源文件第一样通过package 包名声明当前源文件是属于哪个包
2、导入包
scala可以在任何位置导入包
1、导入包下所有类: import 包名.
2、导入包下某个类: import 包名.类名
3、导入包下多个类: import 包名.{类名1,…}
4、导入包下某个类,并起别名: import 包名.{类名=>别名}
5、导入包下除开某个类的所有类: import 包名.{类名=>,}
3、包对象
语法: package object 包名{…}
包对象中定义的非private修饰的属性/方法可以在包中任何位置使用
4、创建包:
语法: package 包名{…}
此种方式创建的包在项目结构看不到,只能在target目录才能看到
5、权限管控
语法: private[包名] val/var 变量名:类型 = 值/private[包名] def 方法名(参数名:类型,..):返回值类型 = {…}
private[包名]代表修饰的成员只能在指定包中使用,其他包不能使用
2、类和对象
1、类
1、定义语法: class 类名{..}
2、定义属性:
语法: [修饰符] val/var 属性名:类型 = 值
var修饰的属性可以通过赋予初始值,在使用赋予初始值的时候必须指定属性的类型
val修饰的属性不可变
var修饰的属性可以改变
3、定义方法
语法: [修饰符] def 方法名(参数名:类型,..):返回值类型 = {…}
4、构造器
scala的构造器分为: 主构造器、辅助构造器
主构造器
定义位置: 主构造器定义在类名后面通过()表示
语法: class 类名([修饰符] [val/var] 属性名:类型[=默认值],…)
val/var修饰的非private的属性与不用val/var修饰的属性的区别:
val/var修饰的非private的属性既可以在class内部使用也可以在class外部使用
不用val/var修饰的属性只能在class内部使用
辅助构造器
定义位置: 定义在class内部
语法: def this(参数名:参数类型,..){
//辅助构造器第一行必须调用其他的辅助构造器或者是主构造器
this(…)
}
5、创建对象: new 类名(…)
2、封装
java很多api底层都会用到java的set/get方法,scala中为了兼容java api,提供了@BeanProperty注解,该注解能够自动生成java的set/get方法。
@BeanProperty注解不能与private一起使用
3、继承、多态
1、继承
1、语法: class 子类 extends 父类
2、final修饰的class不能被继承
3、父类中private修饰的成员不能被继承
4、子类通过override关键字重写父类的方法/val修饰的属性
5、子类要想调用父类的方法通过super关键字调用
2、多态:
val 变量:父类型 = new 子类
4、抽象类
1、语法: abstract class 类名{..}
2、抽象类中既可以定义抽象方法,也可以定义具体方法。既可以定义抽象属性也可以定义具体属性
3、抽象方法: 没有方法体的方法称之为抽象方法[定义抽象方法的时候一般最好写上返回值类型,如果没写默认是Unit]
4、抽象属性: 没有初始化的属性称之为抽象属性
5、匿名子类
new 抽象类{
//重写抽象方法/属性
}
5、单例对象
语法: object object名称{..}
可以通过 object名称 得到单例对象
scala object中所有的属性/方法都是类似java static修饰的,后续调用的是可以通过 object名称.属性名/方法名
scala class中所有的属性/方法都是类似java 非static修饰的,后续调用的是可以通过 对象.属性名/方法名
6、伴生类[class]和伴生对象[object]
伴生类[class]和伴生对象[object]必须满足两个条件:
1、class名称与object名称必须一样
2、class与object必须在同一个源文件中
伴生类和伴生对象可以互相访问对方的private修饰的成员
apply方法: 主要用来简化伴生类对象的创建
apply方法必须定义在伴生对象中
定义apply方法之后,可以通过 object名称.apply(..)/object名称(..) 形式获取伴生类的对象
7、trait
scala中单继承多实现
1、语法: trait 特质名{…}
2、抽象类中既可以定义抽象方法,也可以定义具体方法。既可以定义抽象属性也可以定义具体属性
3、子类实现trait
1、子类要继承class: extends用于继承class,特质的实现通过with关键字
2、子类不需要继承class: extends用于实现第一个特质,后续其他特质的实现通过with关键字
4、特质混入: 指定让某个对象用于特质的所有属性/方法
语法: new class名称(..) with 特质名
5、方法的叠加
scala中可以多实现,如果实现了多个特质,这些特质中都有同名方法,此时子类调用该同名方法的时候默认是报错。
解决问题: 在子类中重写该同名方法
如果在子类中调用父特质的同名方法可以通过 super.方法名(..) 的方式调用,此时默认调用的是继承顺序中最后一个特质的同名方法。
如果想要调用指定父特质的同名方法可以通过 super[特质名].方法名(..) 的方式调用
如果子类继承的多个父特质有关系[有共同的父特质],此时子类在调用同名方法的时候是按照继承顺序从右向左开始调用
6、自身类型: 子类在继承父trait的时候告知必须先继承/实现指定的class/trait
语法: 在特质中通过 this:类型 => 表示
8、扩展
1、类型检查和判断
1、判断对象是否属于某个类型: 对象.isInstanceOf[类型]
2、将对象强转为某个类型: 对象.asInstanceOf[类型]
3、获取对象的class形式: 对象.getClass
4、获取类的class形式: classOf[类型]
2、定义新类型: 给指定类型起别名
语法: type 别名 = 类型
6、集合
scala集合分为两种: 可变集合、不可变集合
可变集合: 集合长度可变,可以增加、删除数据,可变集合在scala.collection.mutable
1、可变数组
创建方式:
1、通过new的方式: new ArrayBuffer元素类型
2、通过apply方法: ArrayBuffer元素类型
获取元素: 数组名(角标)
修改元素: 数组名(角标) = 值
2、可变List
创建方式: 通过apply方法: ListBuffer元素类型
获取元素: 数组名(角标)
修改元素: 数组名(角标) = 值
3、可变Set
Set特点: 无序不重复
创建方式: 通过apply方法: mutable.Set元素类型
4、可变Map
创建方式:
mutbale.MapK的类型,V的类型,(K,V),… )
mutbale.MapK的类型,V的类型
添加元素: put(key,value)
删除元素: remove(key)
获取元素: getOrElse(Key,默认值) 【如果key在map中存在则返回value值,如果key不存在则返回默认值】
Option: scala中为了提醒外部当前返回结果有可能为空,需要进行处理
Some: 代表返回值不为空,返回结果封装在Some中
None: 代表返回值为空
修改元素: updated/update/put
5、可变队列
创建方式: mutable.Queue元素类型
添加元素: enqueue(元素) 【修改原集合】
删除元素: dequeue() 【修改原集合】
获取元素: 队列名(角标)
修改元素: updated/update/队列名(角标)=值
不可变集合: 集合长度不可变,不可以增加、删除数据,不可变集合在scala.collection.immutable
1、不可变数组
创建方式:
1、通过new的方式: new Array元素类型
2、通过apply方法: Array元素类型
获取元素: 数组名(角标)
修改元素: 数组名(角标) = 值
2、不可变List
创建方式:
1、通过apply方法: List元素类型
2、通过::方法: 元素 :: 元素 :: … :: 不可变List/Nil
::最右边一定要是不可变List或者是Nil
Nil是空的不可变List,Nil与不可变List的关系类似Null与String的关系,Nil一般用于给不可变List赋予初始值,在定义变量的时候必须指定类型
添加元素
:: 是添加单个元素
::: 是添加一个不可变List中所有元素
获取元素: list名称(角标)
修改元素: updated(角标,值) 【updated是生成一个新集合,原有集合没有改变】
3、不可变Set
Set特点: 无序不重复
创建方式: 通过apply方法: immutable.Set元素类型
4、不可变Map
创建方式:
MapK的类型,V的类型,(K,V),… )
MapK的类型,V的类型
获取元素: getOrElse(Key,默认值) 【如果key在map中存在则返回value值,如果key不存在则返回默认值】
Option: scala中为了提醒外部当前返回结果有可能为空,需要进行处理
Some: 代表返回值不为空,返回结果封装在Some中
None: 代表返回值为空
修改元素: updated
5、不可变队列
创建方式: immutable.Queue元素类型
添加元素: enqueue(元素) 【生成一个新集合,原集合没有改变】
删除元素: dequeue() 【生成一个新集合,原集合没有改变】
获取元素: 队列名(角标)
修改元素: updated
集合通用方法: +、+:、:+、+=、+=:、-、-=、++、++:、++=、++=:、—、—=
带+与带-的区别:
带+添加元素
带-是删除元素
冒号在前与冒号在后以及不带冒号的区别:
冒号在前是将元素添加在集合最后面
冒号在前是将元素添加在集合最前面
不带冒号是将元素添加在集合最后面
带=与不带=的区别:
带=是修改原集合
不带=是生成一个新集合,原集合没有改变
两个+/-与一个+/-的区别:
一个+/-是添加/删除单个元素
两个+/-是添加/删除一个集合所有元素
元组:
创建方式:
1、通过()方式创建: (初始元素,…)
2、通过->方式创建[只能用于二元元组]: K->V 【scala中二元元组就是KV键值对】
元组一旦创建不能增加、删除、修改元素
元组最多只能装22个元素
获取元素: 元组名.角标 【元组的角标从1开始】
并行集合: 多线程操作集合
scala中集合默认是单线程操作,如果想要多线程操作需要使用并行集合
普通集合转并行集合: 集合名.par
集合常用函数
1、基本属性和操作
判断集合是否为空: isEmpty
判断元素是否存在: contains
将集合转成字符串: mkString(分隔符)
获取集合长度: length/size
2、衍生集合
去重: distinct
删除前N个元素,保留剩余其他所有元素: drop(n)
删除后N个元素,保留剩余其他所有元素: dropRight(n)
获取第一个元素: head
获取最后一个元素: last
获取除开最后一个元素的其他所有元素: init
获取除开第一个元素的所有元素: tail
获取前N个元素: take(n)
获取后N个元素: takeRight(n)
获取指定角标范围的子集合: slice(from,until) [slice获取子集合不包含until角标元素]
滑窗: sliding(size,step)
size: 窗口长度
step: 滑动的长度
交集[两个集合共同的元素]: intersect
并集[两个集合合并]: union [不去重]
差集[A差B的结果就是A中有B中没有的元素]: diff
拉链: zip [如果两个集合一长一短拉链的时候只会拉到最短]
反拉链: unzip
将元素与角标拉链: zipWithIndex
反转: reverse
3、初级函数
获取最大值[是根据元素本身获取]: max
获取最小值[是根据元素本身获取]: min
求和: sum
根据指定字段获取最大值: maxBy(func: 集合元素类型=>B)
maxBy后续是根据函数返回值进行排序之后取最大值
maxBy针对的是集合每个元素
根据指定字段获取最小值: minBy(func: 集合元素类型=>B)
minBy后续是根据函数返回值进行排序之后取最小值
minBy针对的是集合每个元素
排序
1、根据元素本身排序: sorted 【默认升序】
2、根据指定字段排序: sortBy(func: 集合元素类型 => B)
sortBy后续是根据函数返回值进行排序,默认升序
3、根据指定规则排序: sortWith(func: (集合元素类型,集合元素类型)=>Boolean)
sortWith中第一个参数<第二个参数,升序
sortWith中第一个参数>第二个参数,降序
4、高级函数
filter(func: 集合元素类型 => Boolean): 过滤
filter后续会将集合每个元素传递给函数的参数
filter中的函数是针对集合每个元素进行操作,集合有多少元素函数就会调用多少次
filter保留的是函数返回值为true的数据
map(func: 集合元素类型=>B): 映射
map后续会将集合每个元素传递给函数的参数
map中的函数是针对集合每个元素进行操作,集合有多少元素函数就会调用多少次
map针对的场景: 一对一 【集合一个元素进过函数操作之后返回一个结果】
val B =A.map(..) 此时B集合长度=A集合长度
flatten: 压平
flatten只压平第二层集合
flatten针对集合套集合的数据结构
flatten的应用场景: 一对多 【集合一个元素压平之后会变成多个结果】
val B = A.flatten 此时B集合长度>=A集合长度
flatMap(func: 集合元素类型=>集合) = map+flatten: 转换+压平
flatMap后续会将集合每个元素传递给函数的参数
flatMap中的函数是针对集合每个元素进行操作,集合有多少元素函数就会调用多少次
flatMap的应用场景: 一对多 【集合一个元素压平之后会变成多个结果】
val B = A.flatMap(..) 此时B集合长度>=A集合长度
flatMap与flatten的区别:
flatten只是对集合压平,不会对数据进行转换
flatMap是先转换数据,再压平
groupBy(func: 集合元素类型=>B): 按照指定字段分组
groupBy后续会将集合每个元素传递给函数的参数
groupBy中的函数是针对集合每个元素进行操作,集合有多少元素函数就会调用多少次
groupBy分组是根据函数的返回值进行分组
foreach(func: 集合元素类型=>Unit):遍历
foreach后续会将集合每个元素传递给函数的参数
foreach中的函数是针对集合每个元素进行操作,集合有多少元素函数就会调用多少次
foreach与map的区别:
map会返回一个新集合[相当于for循环的yield表达式]
foreach没有返回值[相当于普通for循环]
reduce(func: (集合元素类型,集合元素类型)=>集合元素类型):从左向右聚合
reduce是根据函数进行聚合,每次是两两聚合
reduce函数第一个参数第一次计算的时候初始值=集合第一个元素,后续第N次计算的时候值 = 第N-1次的计算结果
reduceRight(func: (集合元素类型,集合元素类型)=>集合元素类型):从右向左聚合
reduceRight是根据函数进行聚合,每次是两两聚合
reduceRight函数第二个参数第一次计算的时候初始值=集合最后一个元素,后续第N次计算的时候值 = 第N-1次的计算结果
fold(默认值)(func: (集合元素类型,集合元素类型)=>集合元素类型):从左向右聚合
fold函数第一个参数第一次计算的时候初始值=默认值,后续第N次计算的时候值 = 第N-1次的计算结果
foldRight(默认值)(func: (集合元素类型,集合元素类型)=>集合元素类型):从右向左聚合
foldRight函数第二个参数第一次计算的时候初始值=默认值,后续第N次计算的时候值 = 第N-1次的计算结果
7、模式匹配
1、基本语法:
变量名 match {
case 条件 => …
case 条件 => …
case 条件 => …
…
case x => .. //类似java switch的default
}
模式匹配有返回值,返回值就是符合条件的分支的块表达式结果值
模式匹配如果条件满足,会执行条件=>后面的块表达式,块表达式执行完成只有会自动退出
2、守卫
变量名 match {
case 条件 if(…) => …
case 条件 if(…) => …
case 条件 if(…) => …
…
case x => .. //类似java switch的default
}
模式匹配+守卫,后续只有条件满足并且守卫条件也满足才算能够匹配上
3、匹配类型
1、值的匹配
变量名 match {
case 值1 => …
case 值2 => …
case 值3 => …
…
case x => .. //类似java switch的default
}
如果在模式匹配外部定义了一个变量,后续在匹配值的时候想要指定该变量代替具体值,此时变量名必须首字母大写【如果首字母小写,模式匹配会认为是普通变量,此时相当于case x => ..,相当于swith的default】
2、匹配类型
变量名 match {
case x:类型1 => …
case x:类型2 => …
case x:类型3 => …
…
}
3、匹配数组
val arr = ArrayAny
arr match {
case Array(x) => 匹配数组只有一个元素
case Array(x,y,z) => 匹配数组有三个元素 【x,y,z如果在=>右边不使用可以用代替】
case Array(x,_) => 匹配数组至少有一个元素
}
4、匹配List
val arr = ListAny
第一种匹配方式:
arr match {
case List(x) => 匹配数组只有一个元素
case List(x,y,z) => 匹配数组有三个元素 【x,y,z如果在=>右边不使用可以用代替】
case List(x,*) => 匹配数组至少有一个元素
}
第二种匹配方式:
arr match {
case x :: Nil => 匹配数组只有一个元素
case x :: y :: z :: Nil => 匹配数组有三个元素
case x :: tail => 匹配数组至少有一个元素
}
5、匹配元组
val t = (“xx”,10,”yy”)
t match {
case (x,y,z) => ..
}
匹配元组的时候,匹配条件的元组长度必须与变量的元组的长度一致
6、匹配对象和样例类
样例类语法: case class 类名([val/var] 属性名:类型,…)
样例类中如果不用val/var修饰,默认就是val效果的
创建样例类对象: 类名(…)
样例类可以直接用于模式匹配
case class Person(name:String,age:Int)
val p = Person(“lisi”,20)
p match {
case Person(name,age) => …
}
样例类就是伴生类和伴生对象的封装
普通类默认不能直接用于模式匹配,如果想要用于模式匹配需要在伴生对象中定义unapply[将对象结构成属性]方法
class Student(val name:String,val age:Int){}
object Student{
def apply(name:String,age:Int):Student = new Student(name,age)
def unapply(arg:Student):Option[(String,Int)] = {
if(arg==null) None
else Some(arg.name,arg.age)
}
}
val s = Student(“lisi”,20)
s match{
case Student(name,age) => ..
}
7、变量声明的模式匹配
val (name,age) = (“lisi”,20)
val Array(x) = Array(10)
8、偏函数
定义: 没有match关键字的模式匹配称之为偏函数
语法:
val 函数名:PartialFunction[IN,OUT] = {
case 条件 => ..
…
}
IN: 代表函数的参数类型
OUT: 代表函数的返回值类型
偏函数的工作中的应用场景[用于map\flatMap\groupBy等方法中]:
list.map{
case .. => ..
}
8、异常
scala对于异常的处理有两种方式:
1、捕获异常:
1、try{..}catch{case e:XXException => …}finally{…} [一般用于获取外部资源链接的时候使用]
2、Try({代码块}).getOrElse(默认值) [如果代码块出现异常则返回默认值,如果代码块执行成功则返回代码块的执行结果]
2、抛出异常: throw new XXException(“…”) [scala抛出异常不需要在方法名后面通过throws关键字声明]
9、隐式转换
1、隐式转换方法: 自动将一个类型转成另一个类型
语法: implicit def 方法名(参数名: 待转换类型):目标类型 = {…}
隐式转换方法的调用时机:
1、当前类型与目标类型不一致的时候会自动调用隐式转换方法
implicit def double2Int(x:Double):Int = x.toInt
val a:Int = 10.0
此种情况默认报错,此时会自动调用double2Int隐式转换方法,将double转成int
2、对象使用了不属于自身的方法或者属性的时候会自动调用隐式转换方法
class RichFile(file:File){
def getLines():Iterator[String] = Source.fromFile(file,”utf-8”).getLines
}
val file = new File(“..”)
file.getLines() //file没有getLInes方法,此时默认报错
会自动调用隐式转换方法,将File对象转成RichFile对象,此时就可以正常使用getlines方法
2、隐式参数: 在调用方法的时候自动给方法传参
语法:
1、定义方法的时候指定后续给哪个参数自动传参: def 方法名(参数名:类型,..)(implicit 参数名:类型):返回值类型 = {..}
implicit标识的参数,后续在方法调用的时候会自动传参
2、定义隐式参数: implicit val 参数名:类型 = 值
3、隐式类: 将一个类型转成另一个类型
语法: implicit class 类名(属性名:待转换类型){..}
后续scala会调用类的主构造器创建对象,达到类型转换的目的
implicit class Person(s:String){..}
val p:Person = “xxx” //此时会自动调用new Person(“xxx”)从而转换成Person对象
隐式类必须定义在object/class中,不能置于最顶层
隐式转换解析机制:
首先会从当前作用域查找是否有符合条件的隐式转换如果有则直接使用,如果没有则报错
如果隐式转换定义在其他的object中,此时要想使用需要导入: import object名称. / import object名称.隐式转换名称
如果隐式转换定义在其他的class中,此时需要首先创建class的对象,然后通过: import 对象. / import 对象.隐式转换名称
10、泛型
1、泛型方法
语法: def 方法名T,U,..:U = {…}
2、泛型类
语法:
class 类名T,U,..{
def 方法名():U = {..}
}
3、逆变、协变、非变
逆变[-T]: 泛型之间如果是父子关系,通过泛型创建的对象的关系与泛型的父子关系相反
协变[+T]: 泛型之间如果是父子关系,通过泛型创建的对象的关系与泛型的父子关系相同
非变[T]: 泛型之间如果是父子关系,通过泛型创建的对象之间没有任何关系
4、泛型上下限
上限[T<:类型]: 代表T必须是指定类型或者是其子类
下限[T>:类型]: 代表T必须是指定类型或者是其父类
如果需要同时写上下限的时候,先写下限再写上限【T>:类型1<:类型2】
5、上下文
语法:
def 方法名T:类型{…}
等价于
def 方法名T(implicit y:类型[T]){..}