scala教程

1、变量和数据类型
1、注释
单行注释: //
多行注释: //
文档注释: /**…/
2、标识符的命名规范
scala标识符必须是字母、数字、下划线、 、特殊符号 , 首字母不能是数字实际工作中标识符还是采用驼峰原则 3 、变量 1 、定义语法 : v a l / v a r 变量名 : 类型 = 值 2 、 v a l 与 v a r 的区别 : v a l 相当于 j a v a f i n a l 修饰的 , 变量的值不可变 v a r 相当于 j a v a 非 f i n a l 修饰的 , 变量的值可变 3 、在定义变量的时候 , 变量类型可以省略的 , 省略之后 s c a l a 会自动类型推断 : v a l / v a r 变量名 = 值 4 、 s c a l a 在定义变量的时候必须初始化 4 、字符串 s c a l a 获取字符串的方式 : 1 、通过 " " 包裹 : v a l n a m e = " z h a n g s a n " 2 、拼接 1 、通过 + 拼接 : " x x " + " y y " 2 、插值表达式 : s " 、特殊符号,首字母不能是数字 实际工作中标识符还是采用驼峰原则 3、变量 1、定义语法: val/var 变量名:类型 = 值 2、val与var的区别: val相当于java final修饰的,变量的值不可变 var相当于java 非final修饰的,变量的值可变 3、在定义变量的时候,变量类型可以省略的,省略之后scala会自动类型推断: val/var 变量名 = 值 4、scala在定义变量的时候必须初始化 4、字符串 scala获取字符串的方式: 1、通过""包裹: val name = "zhangsan" 2、拼接 1、通过+拼接: "xx"+"yy" 2、插值表达式: s" 、特殊符号,首字母不能是数字实际工作中标识符还是采用驼峰原则3、变量1、定义语法:val/var变量名:类型=2valvar的区别:val相当于javafinal修饰的,变量的值不可变var相当于javafinal修饰的,变量的值可变3、在定义变量的时候,变量类型可以省略的,省略之后scala会自动类型推断:val/var变量名=4scala在定义变量的时候必须初始化4、字符串scala获取字符串的方式:1、通过""包裹:valname="zhangsan"2、拼接1、通过+拼接:"xx"+"yy"2、插值表达式:s"{变量名/表达式}"
3、通过new的方式: new String(“…”)
4、通过一些方式: toString,subString,format…
5、三引号: “”“…”“” [一般用于写sql语句]
5、键盘输入
1、读取控制台数据: StdIn.readLine/readInt/readXXX
2、读取本地文件数据: Source.fromFile(path).getLines
6、数据类型
Any: 所有类的父类
AnyVal: 值类型
Byte、Short、Int、Long、Float、Double、Char、Boolean
Stringops: java 字符串的扩展类
Unit: 相当于java的void,有一个实例()
AnyRef: 引用类型
String,java class/数组/集合,scala class/数组/集合
Null: 所有引用类型的子类,有一个实例null,一般用于给引用类型赋予初始值,在赋予初始值的时候变量类型必须定义
Nothing: 所有类的子类
7、数值与字符串的转换
1、数值与数值的转换
1、低精度转高精度[Int->Long]: 自动转换
2、高精度转低精度[Long->Int]: toXXX方法
2、数值与字符串的转换
1、数值转字符串: toString,拼接[+拼接,插值表达式拼接]
2、字符串转数值: toXXX方法
2、运算符
算术运算符: + - * / %
关系运算符: > < >= <= == !=
逻辑运算符: && || !
赋值运算符: += -= = /= =
位运算符: << >> >>> & |
scala的运算符中没有++、–、三元运算符
scala的运算符是一个个方法,scala的方法的调用的两种方式:
1、对象.方法名(参数值,…)
2、对象 方法名 (参数值,…) [如果方法只有一个参数,()可以省略]
3、流程控制
1、块表达式
1、定义: 由{}包裹的一段代码称之为块表达式
2、块表达式有返回值,返回值是{}中最后一个表达式的结果值
2、分支判断
scala的分支只有if-else,没有switch
单分支: if(…){…}
双分支: if(…){…}else{…}
多分支: if(…){…}else if(…){…}… else{…}
scala的if-else有返回值,返回值就是符合条件的分支的块表达式的结果值
3、for循环
1、for循环的两个重要方法:
1、to方法
语法: start.to(end) / start to end
to方法会生成一个左右闭合的集合
2、until方法
语法: start.until(end) / start until end
until方法会生成一个左闭右开的集合
2、for循环基本语法: for(变量 <- 数组/集合/表达式){…}
3、for循环的守卫: for(变量 <- 数组/集合/表达式 if(布尔表达式)){…}
每次循环的时候都会根据if判断,只有满足条件才会执行循环体
4、步长: for(变量<- start to/until end by step){…}
5、嵌套for循环: for(变量1 <- 数组/集合/表达式;变量2 <- 数组/集合/表达式;…){…}
6、嵌入变量: for(变量1 <- 数组/集合/表达式;变量3 = 值;变量2 <- 数组/集合/表达式;…){…}
7、yield表达式
for循环的循环体的{}默认没有返回值,如果想要返回数据,需要使用yield表达式
语法: for(变量 <- 数组/集合/表达式) yield{…}
4、while与do-while循环
while与do-while循环与java用法完全一样
5、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()

})
}
4、函数式编程
1、方法
1、定义语法: def 方法名(参数名: 参数类型,…):返回值类型 = {方法体}
2、方法的简化原则:
1、如果方法体中只有一行语句,此时方法体的{}可以省略
2、如果使用方法体的块表达式结果值作为方法的返回值,此时返回值类型可以省略
注意: 如果方法体中有return关键字,此时必须定义返回值类型
3、如果方法不需要参数,在定义方法的时候,参数列表的()可以省略
注意:
1、如果在定义方法的时候省略了参数列表的(),那么在调用方法的时候不能带上()
2、如果在定义方法的时候没有省略参数列表的(),那么在调用方法的时候()可有可无
4、如果方法不需要返回值, =可以省略 [=与{}不能同时省略]
3、方法的参数
1、默认值参数: 在后续调用方法的时候,有默认值的参数可以不用传值
语法: def 方法名(参数名: 参数类型=默认值,…):返回值类型 = {方法体}
def m1(x:Int=10,y:Int) = x+y
2、带名参数: 在调用方法的时候指定将值传给哪个参数
m1(y=30)
3、可变参数: 在调用方法的时候,可变参数可以传任意个值
语法: def 方法名(参数名: 参数类型,…,参数名:类型
):返回值类型 = {方法体}
注意:
1、可变参数不能与默认值参数一起使用
2、可变参数必须放在参数列表最后面
3、可变参数不能直接传递数组/集合,如果想要将数组/集合的所有元素传递给可变参数,此时必须通过 集合名:_
的方式传递。
2、函数
1、函数的语法: val 函数名 = (参数名:类型,…) => {函数体}
2、函数是对象,函数的类型: (参数类型,…) => 返回值类型
3、函数的简化: 如果函数体中只有一行语句,此时方法体的{}可以省略
3、方法与函数的区别
1、方法定义在class/object中的时候可以重载,函数是对象,函数名其实就是变量名,同一个作用域不能有多个同名参数,函数不能重载的
2、方法存储在方法区中,函数是对象,存储在堆中
4、方法与函数的联系
1、方法可以定义在方法中,此时方法就是函数
2、方法可以手动转成函数: 方法名 _
5、高阶函数
1、定义: 以函数作为参数/返回值的方法/函数称之为高阶函数
def m1(x:Int,y:Int,func: (Int,Int)=>Int ) = {func(x,y)}
val func = (x:Int,y:Int)=> x+y
m1(10,20,func)
2、简化[在调用高阶函数的时候简化]
1、直接传递函数的值: m1(10,20,(x:Int,y:Int)=> x+y)
2、可以省略函数的参数类型: m1(10,20,(x,y)=> x+y)
3、如果函数的参数在函数体中只使用了一次,那么可以用_代替: m1(10,20,+)
注意,有三种情况不能用_简化:
1、针对函数有多个参数的情况: 函数的参数定义顺序与使用顺序不一致,此时不能用_简化 [第N个_代表第N个参数]
m1(10,20,(x,y)=> y-x)
不能用简化为
m1(10,20,-)
2、针对函数只有一个参数的情况: 函数体中没有做任何操作,直接返回函数的参数,此时不能用_简化
def m2(x:Int,func: (Int)=>Int) = func(x)
m2(10, (x)=>x )
不能简化为
m2(10,)
3、针对函数体中有嵌套的情况: 如果函数的参数在函数体的嵌套中以表达式的形式存在,此时不能用_简化
m2(10, (x)=>(x+1)*10 )
不能简化为
m2(10, (
+1)*10 )
4、如果函数只有一个参数,函数的参数列表的()可以省略
m2(10, (x)=>(x+1)*10 )
简化为
m2(10, x=>(x+1)*10 )
6、匿名函数
定义: 没有函数名的函数称之为匿名函数
匿名函数一般不单独使用,一般作为高阶函数的参数使用
m2(10, x=>(x+1)10 )里面的 x=>(x+1)10 就是匿名函数
7、柯里化
1、定义: 有多个参数列表的方法称之为柯里化
2、语法: def 方法名(参数名:类型,…)(参数名:类型,…)… : 返回值类型= {方法体}
8、闭包
1、定义: 函数体中使用了外部变量的函数称之为闭包
val b = 10
val func = (x:Int)=> x+b
9、递归
定义: 自己调用自己称之为递归
scala递归的前提
1、必须要有退出条件
2、必须定义返回值类型
递归函数必须写在class/object中,不能写在方法中
10、控制抽象
语法: =>返回值类型
控制抽象其实就是一个块表达式,控制抽象是等到调用的时候才会执行,控制抽象可以当做函数调用,在调用的时候不能带上().
控制抽象不能单独使用,必须作为高阶函数的参数使用
11、惰性求值
语法: lazy val 变量名 = 值
lazy修饰的变量在变量没有使用之前不会初始化,不会占用内存空间,只有变量使用的时候才会真正初始化
5、面向对象
1、包
1、导入包
scala可以在任何位置导入包,在某个作用导入包之后,该作用域以及子作用域可以使用该包,其他作用域无法使用
导入包下某个类: import 包名.类名
导入包下所有类: import 包名._
导入包下多个类: import 包名.{类名,类名,…}
导入包下类并起别名: import 包名.{类名=>别名,…}
导入包下除开某个类的所有类: import 包名.{类名=>,}
2、声明包
语法: package 包名
声明包必须在源文件第一行
3、创建包
语法: package 包名{
//创建类和对象
}
此种方式创建的包只能在classes目录才能看到
4、包对象
语法: package object 包名{…}
包对象中定义的非private修饰的成员可以在包中任何位置使用
5、包和访问修饰符结合: private[包名]
类的成员用private[包名]修饰,代表成员只能在指定包中使用
2、类和对象
1、定义类
语法: class 类名{…}
2、创建对象: new 类名(…)
3、类中定义属性与方法
1、定义属性
语法: [访问修饰符] val/var 属性名:类型 = 值
属性如果用var修饰的,可以使用_赋予初始值,此时属性的类型必须定义
scala中没有public修饰符,默认就是public效果的
2、定义方法
语法: [访问修饰] def 方法名(参数名:类型,…):返回值类型 = {…}
4、构造器
scala的构造器分为两种: 主构造器、辅助构造器
主构造器
定义位置: 定义在类名后面
语法: 在类名后面通过 ([访问修饰符] [val/var] 属性名:类型[=默认值],…) 表示
val/var修饰的属性与不用val/var修饰的属性的区别:
val/var修饰的非private属性,可以在class内部和外部都可以调用
不用val/var修饰的属性,只能在class内部使用
辅助构造器
定义位置: 定义在类里面
语法:
def this(参数名:类型,…){
//辅助构造器第一行必须调用其他的辅助构造器或者主构造器
this(…)
}
5、封装
scala中可以使用java api,java很多api底层都需要java set/get方法,所以scala为了兼容java提供了一个注解@BeanProperty,该注解能够自动生成java set/get方法。
@BeanProperty不能修饰在private属性上
6、继承
语法: class 子类 extends 父类
哪些不能被继承:
1、final修饰的class不能被继承
2、父类中private修饰的成员不能被继承
父类中val修饰的属性与方法,子类可以通过override关键字重写[var修饰的属性不能被重写]
子类中如果想要调用父类的成员可以通过super调用
scala的多态是方法和属性都多态,java只是方法多态
7、抽象类
语法: abstract class 类名{…}
抽象类既可以定义抽象方法也可以定义具体方法,既可以定义抽象属性也可以定义具体属性
抽象方法: 没有方法体的方法称之为抽象方法
抽象属性: 没有初始化的属性称之为抽象属性
子类[非抽象类]在继承父类的时候,父类抽象方法和抽象属性,子类必须重写
匿名子类: new 抽象类{
… //重写抽象属性与抽象方法
}
8、单例对象
scala的object标识的就是单例
获取单例对象,可以通过 object名称 获取
scala object中所有属性和方法都是类似java static修饰的
scala class中所有属性和方法都是类似java 非static修饰的
9、伴生类[class]与伴生对象[object]
前提:
1、class与object在同一个源文件中
2、class与object名称必须一样
伴生类与伴生对象可以互相访问对方的私有成员
apply方法:
apply必须定义在伴生对象中
apply主要用于简化伴生类的对象的创建
定义apply方法之后,后续想要创建伴生类的对象可以通过: object名称.apply(…) / object名称(…)
10、特质
1、语法: trait 特质名{…}
2、特质中既可以定义抽象方法也可以定义具体方法,既可以定义抽象属性也可以定义具体属性
3、特质的实现
scala是单继承多实现
1、子类需要继承父class, extends用于继承父class,特质的实现使用with关键字
2、子类不需要继承父class, extends用于实现第一个特质,其他的特质的实现用with关键字
4、特质的混入: 让某个对象拥有指定特质的属性和方法
语法: new 类名(…) with 特质名
5、特质的叠加
子类可以实现多个特质,如果这多个特质都有一个同名方法并且参数列表也一样,此时子类实现之后调用该同名方法的时候默认会报错。【该报错可以在子类中重写同名方法解决】
如果子类想要调用父特质的同名方法可以通过 super.方法名 方式调用,此时调用的是最后一个特质的同名方法,如果想要调用指定特质的同名方法可以通过 super[父特质名].方法名 的方式调用
如果子类实现的多个父特质都继承自同一个父特质,并且重写了同名方法,此时通过super.方法名调用的时候,同名方法的调用顺序是按照继承顺序从右向左依次执行。
6、自身类型: 提醒子类在继承该特质的时候必须提前继承/实现指定class/特质
语法: 在特质中通过 this:类型 => 表示
11、扩展
1、类型检查和判断
1、判断对象是否属于某个类型: 对象.isInstanceOf[类型]
2、将对象强制转成某个类型: 对象.asInstanceOf[类型]
3、获取类的class形式: classOf[类名]
4、获取对象的class形式: 对象.getClass
2、定义新类型: 给类起别名
语法: type 别名 = 类名
6、集合
scala中的集合分为两种: 可变集合、不可变集合
不可变集合: 集合的长度不可变,不能添加/删除数据 [不可变集合scala.collection.immutable]
1、不可变数组
创建方式:
1、通过new的方式: new Array元素类型
2、通过apply方法: Array元素类型
获取指定角标元素: 数组名(角标)
修改指定角标元素: 数组名(角标) = 值
2、不可变List
创建方式:
1、通过apply方式: List元素类型
2、通过::方法: 初始元素 :: 初始元素 :: … :: Nil/不可变List
::最右边必须是不可变List或者是Nil
Nil可以当做一个不可变的空的List
Nil与不可变List的关系类似Null与String,后续Nil可以用于给不可变List赋予初始值,在赋予初始值的时候变量的类型必须定义
获取指定角标元素: list名称(角标)
添加元素:
:: 代表是添加单个元素,是生成新集合,原集合没有改变
::: 代表是添加一个集合所有元素,是生成新集合,原集合没有改变
3、不可变Set
Set是无序不重复的
创建方式: immutable.Set元素类型
4、不可变Map
创建方式: immutable.Map[K的类型,V的类型]( K->V, (K,V),… )
获取数据
1、根据key获取value值: getOrElse(key,默认值) 【key如果在map中存在则返回value值,如果不存在则返回默认值】
2、获取所有key: keys
3、获取所有value值: values
5、不可变队列
创建方式: immutable.Queue元素类型
添加元素: enqueue(元素) [原集合没有改变,生成了新集合]
删除元素: dequeue() [原集合没有改变,生成了新集合]
获取元素: 队列名(角标)
修改元素: 队列名.updated(角标,值)
可变集合: 集合的长度可变,可以添加/删除数据 [可变集合scala.collection.mutable]
1、可变数组
创建方式:
1、通过new的方式: new ArrayBuffer元素类型
2、通过apply方法: ArrayBuffer元素类型
获取指定角标元素: 数组名(角标)
修改指定角标元素: 数组名(角标) = 值
2、可变List
创建方式:
1、通过apply方式: ListBuffer元素类型
2、通过new的方式: new ListBuffer元素类型
获取指定角标元素: list名称(角标)
修改指定角标元素: list名称(角标) = 值
3、可变Set
Set是无序不重复的
创建方式: mutable.Set元素类型
4、可变Map
创建方式: mutable.Map[K的类型,V的类型]( K->V, (K,V),… )
获取数据
1、根据key获取value值: getOrElse(key,默认值) 【key如果在map中存在则返回value值,如果不存在则返回默认值】
2、获取所有key: keys
3、获取所有value值: values
5、可变队列
创建方式: mutable.Queue元素类型
添加元素: enqueue(元素)
删除元素: dequeue()
获取元素: 队列名(角标)
修改元素: 队列名(角标) = 值 / 队列名.update(角标,值)
集合通用的添加[+、+:、:+、+=、+=:、++、++:、++=、++=:]/删除[-、-=、–、–=]方法的区别
带+[+、+:、:+、+=、+=:、++、++:、++=、++=:]与带-[-、-=、–、–=]的区别:
带+是添加元素
带-是删除元素
一个+/-[+、+:、:+、+=、+=:、-、-=]与两个+/-[++、++:、++=、++=:、–、–=]的区别:
一个+/-是添加/删除单个元素
两个+/-是添加/删除一个集合所有元素
带=[+=、+=:、++=、++=:、-=、–=]与不带=[+、+:、:+、++、++:、-、–]的区别:
带=是添加/删除元素到原集合中
不带=是生成新集合,原集合中没有任何改变
不带冒号[+、+、++、++=、+=]与冒号在前[:+]以及冒号在后[+:、+=:、++=:]的区别:
不带冒号是将元素添加在集合最后面
冒号在前是将元素添加在集合最后面
冒号在后是将元素添加在集合最前面
update与updated的区别:
update是直接修改原集合元素
updated是生成新集合,原集合没有改变
集合常用属性与方法
1、集合常用属性
判断集合是否包含某个元素: contains
获取集合元素个数: size/length
判断集合是否为空: isEmpty
将集合元素转成字符串: mkString(“分隔符”)
将集合转成迭代器: toIterator/toIterable
iterator: 只能使用一次
Iterable: 可以重复使用
2、衍生集合
去重: distinct
获取除开前N个元素的其他所有元素: drop(n)
获取除开后N个元素的其他所有元素: dropRight(n)
获取第一个元素: head
获取最后一个元素: last
获取除开最后一个元素的其他所有元素: init
获取除开第一个元素的其他所有元素: tail
获取前N个元素: take(n)
获取后N个元素: takeRight(n)
获取指定角标范围的所有元素: slice(startIndex,stopIndex) [结果集不包含stopIndex对应的元素]
滑窗: sliding(size,step)
size: 窗口大小
step: 滑动长度
反转: reverse
交集: intersect
差集: diff
并集: union
拉链: zip
反拉链: unzip
将元素与角标拉链: zipWithIndex
3、集合初级计算函数
获取最大值: max
获取最小值: min
求和: sum
根据指定字段获取最大值: maxBy(func: 集合元素类型=>B)
maxBy里面的函数是针对集合每个元素操作
后续maxBy是根据函数的返回值获取集合最大元素
根据指定字段获取最小值: minBy(func: 集合元素类型=>B)
minBy里面的函数是针对集合每个元素操作
后续minBy是根据函数的返回值获取集合最小元素
排序
1、直接根据集合元素本身排序[一般用于集合元素是数字类型]: sorted [默认升序]
2、根据指定字段排序[一般用于集合元素类型是字符串,元组等类型]: sortBy(func: 集合元素类型=> B) 【重点】
sortBy里面的函数是针对集合每个元素操作
后续sortBy是根据函数的返回值进行排序
3、根据指定规则排序: sortWith(lt: (集合元素类型,集合元素类型)=>Boolean)
升序: 第一个参数<第二个参数
降序: 第一个参数>第二个参数
4、集合高级计算函数
map(func: 集合元素类型=> B ): 一对一映射
map里面的函数是针对每个元素操作,元素有多少个,函数就调用多少次
map会生成一个新的集合,新集合元素个数 = 原集合元素个数
map的应用场景: 一般用于数据类型/值的转换
flatten: 压平
flatten只可以用在集合嵌套集合的数据类型上
flatten是将第二层集合压掉
flatten生成的新集合元素个数>=原集合元素个数
flatten的应用场景: 一对多
flatMap(func: 集合元素类型=> 集合) = map + flatten
flatMap里面的函数是针对每个元素操作,元素有多少个,函数就调用多少次
flatMap生成的新集合元素个数>=原集合元素个数
flatMap的应用场景: 一对多
flatten与flatMap的区别:
flatten是针对集合嵌套集合的数据类型,不会对集合元素进行转换,只是单纯压平
flatMap可以针对字符串等类型操作,是压平之前先对元素进行转换
foreach(func: 集合元素类型=> B):Unit : 对集合元素遍历
foreach里面的函数是针对每个元素操作,元素有多少个,函数就调用多少次
foreach与map的唯一区别:
map会生成一个新的集合[map相当于有yield关键字的for循环]
foreach没有返回值[map相当于没有yield关键字的for循环]
filter(func: 集合元素类型=> Boolean): 根据指定条件过滤
filter里面的函数是针对每个元素操作,元素有多少个,函数就调用多少次
filter保留的是函数返回值为true的数据
groupBy(func: 集合元素类型=> K ): 按照指定字段分组
groupBy里面的函数是针对每个元素操作,元素有多少个,函数就调用多少次
groupBy生成一个Map[K,V]
K是函数返回值
V是K在原集合中对应的所有元素
reduce(func: (集合元素类型,集合元素类型)=>集合元素类型 ): 从左向右聚合
reduce里面的函数的第一个参数是上一次聚合结果[第一次聚合的时候,第一个参数的初始值 = 集合第一个元素],第二个参数是当前待聚合的元素
reduceRight(func: (集合元素类型,集合元素类型)=>集合元素类型 ): 从右向左聚合
reduceRight里面的函数的第二个参数是上一次聚合结果[第一次聚合的时候,第二个参数的初始值 = 集合最后一个元素],第一个参数是当前待聚合的元素
fold(默认值)(func: (集合元素类型,集合元素类型)=>集合元素类型 ): 从左向右聚合
fold里面的函数的第一个参数是上一次聚合结果[第一次聚合的时候,第一个参数的初始值 = 默认值],第二个参数是当前待聚合的元素
foldRight(默认值)(func: (集合元素类型,集合元素类型)=>集合元素类型 ): 从右向左聚合
foldRight里面的函数的第二个参数是上一次聚合结果[第一次聚合的时候,第二个参数的初始值 = 默认值],第一个参数是当前待聚合的元素
元组
创建方式:
1、通过()方式: (初始元素,…)
2、通过->的方式[只能用于创建二元元组]: K -> V
scala二元元组是当做KV键值对看
元组最多只能存储22个元素
元组一旦定义,元组的长度与元素都不可以改变
元组获取元素可以通过: 元组名.N [N就是元素的角标,元组的角标从1开始]
并行集合:
scala集合默认情况下只能单线程操作,如果想要多线程操作,需要转换为并行集合: 集合名.par
7、模式匹配
1、基本语法:
变量 match {
case 条件1 => {…} //每个分支的块表达式的{}可以省略

case _ => … //代表默认情况
}
模式匹配有返回值,返回值就是符合条件的分支的块表达式的结果值
2、守卫: 在case条件后面加上if判断语句
变量 match {
case 条件1 if(布尔表达式) => {…} //每个分支的块表达式的{}可以省略

case _ => … //代表默认情况
}
后续在匹配条件的时候首先要满足case条件 然后还要满足if条件才算能匹配上
3、模式匹配的应用
1、匹配值
val Xy = “hello”
变量 match {
case 值1 => …
case Xy => …

case x => … //代表默认情况
}
如果在匹配值的时候,想要使用外部变量的值代替case条件,此时变量名首字母必须大写
如果case条件变量名首字母是小写,此时该变量是局部变量,代表模式匹配的默认情况
2、匹配类型[匹配变量的类型是啥]
变量 match {
case x:类型1 => …
case x:类型2 => …
case x:类型3 => …

}
3、匹配数组
数组名 match {
case Array(x) => 匹配数组只有一个元素
case Array(x,y,z) => 匹配数组有三个元素
case Array(x,
) => 匹配数组至少有一个元素
}
变量x,y,z如果在=>右边不使用可以使用_代替
4、匹配List
第一种匹配方式:
list名称 match {
case List(x) => 匹配list只有一个元素
case List(x,y,z) => 匹配list有三个元素
case List(x,_
) => 匹配list至少有一个元素
}
第二种匹配方式:
list名称 match {
case x :: Nil => 匹配list只有一个元素
case x :: y :: z :: Nil => 匹配list有三个元素
case x :: tail => 匹配list至少有一个元素
}
5、匹配元组
元组名 match {
case (…) => …
}
变量是几元元组,匹配的时候条件就应该是几元元组
6、匹配样例类和对象
样例类: 其实就是伴生类和伴生对象的封装
语法: case class 类名([val/var] 属性名:类型,…)
属性如果没有用val/var修饰,默认就是val修饰的
创建样例类对象: 类名.apply(值,…) / 类名(值,…)
样例对象
语法: case object object名称
样例类可以直接用于模式匹配
case class Person(name:String,age:Int)
val p = Person(“zhangsan”,20)
p match {
case Person(name,age) => …
}
scala普通类不能直接用于模式匹配,如果想要用于模式匹配需要在伴生对象中定义unapply[将对象解构成属性]方法
class Student(val name:String, val age:Int)
object Student{
def apply(name:String,age:Int) = new Student(name,age)

			def unapply(stu:Student): Option[(String,Int)] = {
				if(stu==null) None
				else Some( (stu.name,stu.age) )
			}
		}
		val stu = Student("lisi",30)
		stu match {
			case Student(name,age) => ..
		}
	7、偏函数
		定义: 没有match关键字的模式匹配称之为偏函数
		语法: 
			val 函数名:PartialFunction[IN,OUT] = {
					case 条件1 => ..
					....
			}
			IN: 代表函数参数类型
			OUT: 代表函数返回值类型
		偏函数一般集合map/flatMap/groupBy等高阶函数一起使用,用于匹配元组等类型
		val list = List[(String,Int)]( ("lisi",20),... )
		list.map{
			case (name,age) => name
		}

8、异常
scala处理异常的方式有两种:
1、捕获异常
1、try{…}catch{case e:XXException=>…}… finally{…} [此种方式一般只用于获取外部资源链接的场景<操作Mysql/hbase等>]
2、Try(代码块).getOrElse(默认值) [常用,一般用于补全缺失值]
2、抛出异常: 只需要在方法体中通过throw关键字抛出异常即可,不需要在方法名后面通过throws关键字声明异常
9、隐式转换
1、隐式转换方法: scala自动调用方法,将一个类型转成另一个类型
语法: implicit def 方法名( 参数名: 待转换类型 ): 目标类型 = {…}
隐式转换方法的调用时机:
1、当当前类型与目标类型不一致的时候,此时scala会自动调用隐式转换方法转换类型
val d:Int = 2.0
2、当对象使用了不属于自身的属性和方法的时候,此时scala会自动调用隐式转换方法转换类型
val file = new File(…)
file.getLines() //file对象本身没有getLines方法,所以会查询是否有隐式转换方法能够将File对象转成某一个对象,该对象拥有getLines方法
2、隐式参数: 在调用方法的时候,scala会自动给方法参数传值
语法:
1、在定义方法的时候指定方法的哪个参数后续scala会自动传值[implicit标识的参数后续scala会自动传值]
def 方法名(参数名:类型,…)(implicit 参数名:类型) = {…}
2、定义后续scala将哪个值传给隐式参数
implicit val 参数名:类型 = 值
3、隐式类: scala自动初始化隐式类对象,从而将一个类型转成隐式类类型
语法:
implicit class 类名( 属性名: 待转换类型 ) {…}
后续如果待转换类型的对象使用了隐式类对象的方法/属性的时候,scala会自动调用隐式类的主构造器创建隐式类对象,从而实现待转换类型与隐式类类型的转换
隐式类必须定义在class/object中,不能置于最顶层
隐式转换的解析机制:
1、首先从当前作用域以及父作用域中查询是否有符合要求的隐式转换,如果有则直接调用,如果没有则报错
2、如果隐式转换定义在其他的object/class中,此时要想使用的必须导入
1、如果隐式转换定义在object中,可以通过 import object名称._ / import object名称.隐式转换名称 导入
1、如果隐式转换定义在class中,可以通过 import 对象名._ / import 对象名.隐式转换名称 导入
如果找到多个隐式转换都符合条件,此时必须明确指定使用哪一个隐式转换 import 对象名.隐式转换名称/import object名称.隐式转换名称
10、泛型
1、泛型方法: 后续在调用方法的时候,方法的参数或者返回值类型可以不固定
语法: def 方法名T,U,.:U = {…}
2、泛型类: 泛型类的属性/方法的参数/返回值都可以使用泛型
语法
class 类名T,U,…{
def 方法名(参数:T,…):U = {…}
}
3、泛型上下限
上限[T<:类型] : 代表泛型的类型必须是指定类型或者是其子类
下限[T>:类型] : 代表泛型的类型必须是指定类型或者是其父类
4、逆变、协变、非变
非变[T]: 代表创建同一个类的对象的时候,如果泛型不一样,创建出来的对象之间没有任何关系
协变[+T]: 代表创建同一个类的对象的时候,如果泛型之间有父子关系,创建出来的对象之间继承了泛型的父子关系
逆变[-T]: 代表创建同一个类的对象的时候,如果泛型之间有父子关系,创建出来的对象之间颠倒了泛型的父子关系
5、上下文: 自动生成一个隐式参数
语法: T:类型 【后续会自动生成一个隐式参数 implict 参数名: 类型[T]】
def m1T:Person{
val p = implicity[Person[T]]

}
等价于
def m1T(implicit p: Person[T]){

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值