Scala中函数是一等公民,可以不依赖对象直接创建函数,Scala将函数式编程和面向对象编程融合在一起。Scala中将对象属性和对象方法与类属性和类方法进行了分离,Scala中的对象相关属性和方法通过 class关键字定义在对象域中,而类相关的属性和方法通过 object关键字定义在 伴生对象中(Scala中没有 static关键字,伴生对象编译后会生成 原始对象的class文件(类名.class)和 伴生对象的class文件(类名$.class)笔记语法中的”“ 代表用户必填项,”[xxx]“ 代表选填项
一、基本语法
声明变量:var|val [: 变量类型] = [变量值]
Scala中分号可不写val代表不可变量,编译后加上 final修饰
特殊类型说明
Unit
等同于 void
Null
引用类型,但只有一个null实例
Nothing
在Scala继承链最底层,是任何类的子类,利用 多态在我们确定一个函数不会正常返回的时候,可以将返回值(异常)返回给其他变量(因为是子类)
Scala中值类型转换
向下转型直接赋值
向上转型在值上调用 toXxx(如:toDouble、toInt)强制转换函数
Scala中不支持三目运算符(即:?:),全部使用 if-else方式实现Scala中也没有 switch语句,而是使用 match-case匹配模式实现
Scala中for循环控制
// 打印1到3,闭区间
for(i
print(i);
}
// 打印1到3,前闭后开
for(i
print(i)
}
// 直接遍历集合
val list = List("h",1,2.2);
for(item
println(item)
}
// 循环守卫(也称条件判断式),判断为true进入循环,false则跳过循环
for(i
println(i);
}
// 循环返回值,对循环中处理的结果返回到一个新的Vector集合中
var res = for(i
// 控制步长,打印1到10 步长为2
for(i
println(i)
}
二、函数式编程基础
在Scala中函数也被当做一种数据类型,可以作为方法的参数传递,Scala中将没有返回值的函数/方法也称为 过程
在类中定义方法:
def ([数名称:参数类型]....)[:返回值类型[=]]{[语句...][return 返回值]}
直接定义函数:
val func ([数名称:参数类型]....)[:返回值类型] =>{[语句...][return 返回值]}
将对象的方法转换为独立的函数:
val func = obj.func _;
形参为空可以省略()
def ([数名称:参数类型]....)={...}表示返回值类型不确定(最后一条语句不能带 return关键字),运行时类型推导完成,语句可以返回 多种类型
def ([数名称:参数类型]....):{...}表示返回值类型在编译期就确定,语句中只能返回 一种类型
def ([数名称:参数类型]....){...}表示没有返回值(即等价于返回值Unit),即使语句中有return也不生效,这种函数也叫 过程
如果没有return语句,默认语句的最后一行结果作为返回值
如果写了语句最后写了return关键字,则返回值类型就不能省略,函数返回不能自行推导
形参列表和返回值的数据类型可以是值类型和引用类型
在Scala中声明函数/方法形参列表时,可以指定默认值
可变参数写法
def (参数名:参数类型*){[语句...]}
惰性函数
在将函数返回值赋值给其他变量时候,将这个变量声明为lazy,则该函数不会立即执行,只会在取值的时候才会计算,这种函数叫 惰性函数
lazy可以修饰val变量,但不能修饰var变量。指定了该变量后也会在用到的时候才会赋值
三、类与对象
3.1 基础
Scala没有public关键字,定义类的时候 属性默认为private修饰并且默认编译后会提供 属性名()的方法用来获取该方法,和 属性名_$eq()方法用来设置该属性,如果定义属性时添加上private则编译后不会再添加这两个方法,需要开发者自己提供。定义类的时候 方法默认为public修饰,protected修饰的属性或者方法只能对子类访问,而子包不能访问Scala还通过object关键字来定义 伴生对象,将所有类属性和类方法全部定义到 伴生对象中
[:类型] = new ();
3.2 构造器
Scala包含 主构造器和 辅助构造器,在scala中体现了函数编程和面向对象编程的思想结合,定义主构造器放在类名之后,且 主构造器会执行直接放在类中的所有语句。伴生对象可以通过def apply():={}方式定义工厂方法,通过 伴生对象创建对象时不用new直接使用类名(实参列表)形式
1.想让主构造器变私有,则在()前加上 private2.主构造器无参数可以省略类名后面的()3.构造器参数未使用任何访问符,则形参是局部变量4.构造器参数使用var定义会则编译后会将变量作为private属性并提供 xxx()方法和xxx_$eq()方法5.构造器参数使用val定义则编译后变量用private final修饰并提供 xxx()方法6.创建对象时必须为属性指定初始值,也可以用 -赋值初始值7.只有 主构造器可以调用 父类构造器,但是不能通过super调用,可以通过extends 父类(参数)的方式给符类构造器传递参数8*.在类上加上@BeanProperty注解,会自动生成setter和getter方法
3.3 包
3.3.1 包定义
Scala中的包可以嵌套使用,子包可以 直接访问父包内容,而父包要通过import来访问子包内容,并且引入包的时候,只能访问该包下面的类,而不能递归访问子包下面的类(即import不是精确引入,不会递归引入)包中可以包含类\对象\特质(trait),但是不能包含变量和方法,这是虚拟机局限,因此Scala通过包对象来解决.(每一个包只能有一个包对象)包中对象可以直接调用包对象定义的属性和方法
定义语法:package object {[语句...]}
定义方法和属性时候private和protected修饰的成员后面可以通过 [包名]来扩大可见性
3.3.2 包引入
Scala中的包可以在任何需要的地方引入,从而降低import包的作用范围。
// Scala中可以在引包的时候将类定义别名
import java.utils.{HashMap=>JavaHashMap}
// Scala在引入包的时候可以制定忽略某些类
import java.utils.{HashMap=_,_}
3.4 继承
Scala只支持单继承使用extends关键字,子类继承了父类所有的属性和方法,只是私有属性和方法不能在子类访问Scala中不存在Java继承时属性存在的动态绑定问题,因为Scala继承属性的时会把属性的属性名()和属性名_$eq()一起继承过去,相当于 重写方法Scala中引用类型强制类型转换通过asInstanceOf[父类]来转换,通过isInstanceOf来判断类型Scala使用trait关键字声明特质(等同java中的接口),当一个类继了特质,就可以用该特质来引用该类的实例对象
声明时使用特质:class extends with with with
创建对象时使用特质(动态混入): 对象名 = new 类名 with with 创建对象时按照定义顺序从左到右加载(初始化特质),而调用覆盖的特质方法时,会从右向左调用,程序运行过程中不管有无继承关系特质2中 super指的是特质1 ,如果想让特质2指向自己的 真实父类,可以通过 泛型即:super[特质直接父类].xxx(...)的方式调用
既有抽象方法又有具体方法的特质也叫 富接口混入了特质的类,特质中的属性直接加入该类作为类的属性(对象属性或者类属性具体看特质如何定义),并且特质中抽象的字段(未赋值)在具体的类中必须初始化
特质可以继承具体的类(相当于Java中的接口可以继承具体的类),然后可以扩展类的功能,混入该特质的类只能继承该特质的超类
特质继承了具体的类,如果想限制实现该特质的类 必须是特质超类的子类才能调用该方法,则可以在方法前加上this.=>的限定
3.5 内部类
// 创建内部类
new Outter().Inner()
// 创建静态内部类,在伴生对象中定义
new Outter.Inner()
通过 别名=>class 内部类的形式将 外部类.this替换为前面的 别名,这种方式在定义 别名的时候要将外部类属性放在别名定义之后,否则编译错误。非静态内部类默认与 外部类对象关联,如果方法需要调用自身 非静态内部类对象,即屏蔽外对象对内部类对象影响可以通过类型投影,具体做法就是在 非静态内部类调用自身的方法的形参前面加上 外部类#前缀,表示忽略内部类对象之间关系。
3.6 隐式
1. 隐式转换函数式通过implicit声明 有且只有一个参数的函数,这种函数在它作用域范围内 自动调用,因为是 自动调用它的名称并不重要。只是与 函数签名有关(函数的 参数列表以及 返回值)
implicit def ( ):{[语句...]}
2. 通过implicit声明的变量叫 隐式值,如果函数要使用 隐式值需要在 形参前面也加上 implicit,这样调用该 拥有隐式值形参的函数在调用时如果没有传入参数,则会在作用范围内寻找通过implicit声明的 隐式值作为实参。隐式值的优先级要大于定义形参时指定的 形参默认值
3. 隐式类通过`implicit`声明在类、伴生对象、包对象中(即:隐式类不能作为顶级类),**主构造器** 只能包含一个参数作为在该范围内被识别并转换的类型(形参不需要`implicit`修饰),在 **隐式类** 作用范围内通过 **隐式类主构造器参数类型的对象** 调用 **隐式类中的方法**(在该类型对象中没定义该方法),则在编译过后会通过 **将该类型对象作为参数传入隐式类,然后通过隐式类调用隐式类中方法** 的方式调用 **隐式类中的方法**,来在隐式类作用范围内增强指定对象
四、集合基本使用
在Scala中集合分为两种:变长集合、定长集合,并且有时候会用到Java中的集合,则会通过 隐式转换在两者之间互相转换。
4.1 Array
// 直接创建定长数组(泛型是可选的,但必须指定集合长度),不能直接print全部元素
val array = new Array[Int](3);
// 通过集合伴生对象的apply方法创建定长数组,可以直接print全部元素
val array = List(1,2,3);
// 定义多维数组(只能是定长)
val arrayDim = Array.ofDim[Int](3,4) //3行4列,元素Int类型
// 定长集合是定义在scala包中的不需要引入,而变长集合需要单独引用
import scala.collection.mutable.ArrayBuffer
// 定长集合转换为变长集合
val arrayBuffer = array.toBuffer;
// 创建变长数组
val arrayBuffer = new ArrayBuffer[Int](3);
// 向变长数组添加元素
arrayBuffer.append(1);
// 变长集合转换为定长集合
val array = arrayBuffer.toArray;
// Scala数组转换为Java数组(因为Java数组是变长,所以只能中Scala中的ArrayBuffer转换)
// 导入隐式转换函数
import scala.collection.JavaConversions.bufferAsJavaList
val scalaArr = new ArrBuffer[Int](3);
// 该方法会需要Java的ArrayList,调用时会被引入的隐式转换函数识别转换为ArrayList
val javaArr = new ProcessBuilder(scala);
// Java数组转换为Scala数组
import scala.collection.JavaConversions.asScalaBuffer
val scalaArr:mutable.Buffer[Int] = javaArr;
4.2 Tuple
元组是一个容器可以存放相同或者不同的元素作为一个整体,Scala中元组最大可以存储22个元素,Tuple1~Tuple22不同的元素个数是不同的元组类型
// 元组定义
val tuple = (1,2,"hello",4);
// 元组访问
print(tuple._2);// 访问元组第二个元素
4.3 List
默认情况下Scala中的List是不可变(如果用可变则需要ListBuffer),并且是一个对象可以直接存储数据。
//--------------------不可变List
// 创建list
val list = List(1,2,3);
// 创建空集合
val list = Nil;
// 访问list元素
print(list(1));//访问第二个元素
// 在list之后追加数据
val list2 = list:+4;//list并没有改变,而是返回一个添加元素后的新集合
// 在list之前添加数据
val list = 4+:list
// 将每一个元素加到集合中
val list = 1::2::3::List(1,2,3)::Nil;
// 将集合分解,并把集合中的每个元素添加到指定末尾集合中
val list = 1::2::3::List(1,2,3):::Nil;
//----------------------可变ListBuffer
import scala.collection.mutable.ListBuffer
// 创建可变list
val listBuffer = ListBuffer(1,2,3);
// 从可变list删除元素
listBuffer-=1;
// 向可变list添加元素
listBuffer+=1;
// 合并另外一个集合(可以是不变list)
listBuffer++list;
4.4 Queue
在Scala中有scala.collection.mutable.Queue和scala.collection.immutable.Queue两种队列,一般开发中用到的是可变队列
// 创建可变队列
import scala.collection.mutable.Queue
val queue = Queue(1,2,3);
// 向队尾增加元素
queue+=4;
// 向对位添加集合中的所有元素
queue++=List(1,2,3);
// 从队列头部取出元素
queue.dequeue();
// 向队列尾部添加元素
queue.enqueue(4,5,6);
// 返回队首元素(对队列无影响)
queue.head;
// 返回队尾元素(对队列无影响)
queue.last;
// 返回除第一个意外的所有元素
queue.tail;
4.5 Map
在Scala中有scala.collection.mutable.Map和scala.collection.immutable.Map两种Map,默认是不可变Map,其中不可变Map有序,可变map无序。在Scala底层Map元素是Tuple2类型
// 创建Map
val map = Map("a"->10,"b"->20);
// 以二元组形式创建Map
val map = Map(("a",10),("b",20));
// 使用map取值,如果key不存在则抛出异常
map.map("a");
// 检查元素是否存在
map.contains("a");
// get取值,如果存在返回some,不存在返回none
map.get("a").get
// getOrElse取值,如果不存在返回默认值(第二个参数指定的值)
map.getOrEles("a","默认值");
// 添加元素
map+=("c"->30,"d"->40);
// 删除元素
map-=("c"->30,"d"->40);
// 取出的元素是Tuple2类型,因此遍历可以通过元组的方式访问
for(ele
4.6 Set
在Scala中有scala.collection.mutable.Set和scala.collection.immutable.Set两种Map,默认是不可变Map
// 创建可变set
import scala.collection.mutable.Set
val set = Set(1,2,3);
// 向set中添加元素
set+=4;
// 在set中删除元素
set-=4;
###
五、集合高级使用
5.1 映射操作
将集合中的每一个元素通过 映射函数转换为新结果集合
val list = List(1,2,3);
list.map(x=>x+1)
5.2 扁平化
val list = List(1,2,3);
list.flatMap(_.toString);
5.3 元素过滤
val list = List(1,2,3);
list.filter(_>2);
5.4 化简
val list = List(1,2,3);
// 左边第一个元素开始迭代向后执行函数
list.reduce(_+_);//等同于reduceLeft
5.5 折叠
val list = List(1,2,3);
list.fold(4)(_+_);//等价于在集合左侧添加元素后执行第二个函数参数
4/:list(_+_)
5.6 扫描
// 对集合的每个元素进行fold操作,让后将所有中间结果保存到一个集合中
val list = List(1,2,3);
val list2 = list.scan(4)(_+_);
5.7 拉链合并
val list1 = List(1,2,3);
val list2 = List(4,5,6);
list1.zip(list2);//配对形成元组集合(1,4),(2,5),(3,6)
5.8 迭代器
迭代器遍历元素只能访问一次,第二次访问需要重新获取迭代器
val list = List(1,2,3);
val iterator = list.iterator;
while(iterator.hasNext){
iterator.next()
}
// 重新获取迭代器,以临另一种方式迭代
for(ele
ele;
}
5.9 流
steam是一个存放无穷元素的集合,主要用来通过指定规则动态生成元素。末尾元素遵循lazy规则
def numsForm(n:BigInt):Stream[BigIni]=n#::numsForm(n+1)
numsFrom(4).tail
5.10 视图
使用视图对集合的所有操作并不会立即执行,而是在用到集合元素的时候才去执行
val list = List(1,2,3)
list.view.filter(_>1);
5.11 并行集合
val list = List(1,2,3);
list.par.map(_+2);
5.12 操作符扩展
class oper{
var money:Int = _;
// 对中置操作符重载
def +(n:Int):Unit={
this.money+=n;
}
// 对后置操作符重载
def ++():Unit={
this.money+=1
}
// 对前置操作符重载,方法前需要加上unary_前缀
def unary_!():Unit={
this.money=-money;
}
}
六、模式匹配
6.1 基本介绍
val oper = '+';
oper match{
case '+'=>print("加法");
// 如果都没匹配,最终会执行 case_后面的语句
case _=>
}
6.2 守卫
val num =120
num match{
//此时的_不表示默认匹配
case _if(mun>100)=>print(num)
case _=>
}
6.3 模式中的变量
val a = "aaaa"
a match{
// 将a中的值传递给mystring
case mystring => print(mystring);
case _ =>
}
val b = ‘b’
b match{
// match中可以有返回值,默认case的最后一条语句为返回值
case ‘b’ => "hello";
case _ =>
}
6.4 匹配集合
Array(0) // 匹配只有一个元素且为0的数组
Array(x,y) // 匹配数组有两个元素并将两个元素赋值x,y
Array(0,_*) // 匹配数组以0开始
0::Nil // 匹配只有一个元素且为0的List
x::y::Nil // 匹配有两个元素并将两个元素赋值为x,y
0::tail // 匹配第一个元素为0的List
(0,_) // 匹配第一个元素为0的tuple2
(y,0) // 匹配第二个元素为0的tuple2,并把第一个元素赋值为x
6.5 对象匹配
// 伴生对向的unapply方法返回Some集合为匹配成功,返回None集合为返回失败
Object Square{
def unapply(d:Double):Option[Double]={
Some(math.qurt(d))
}
}
val num = 10.1
num match{
// 调用Square的unapply方法隐式传入num,返回结果Some的值赋给n
case Square(n) => print(n);
case _ =>
}
6.6 样例模式
// 声明样例类后面必须带一个参数,该参数默认用val修饰作为不可变属性
case class A(v:Int){}
6.7 密封类
通过sealed关键字声明的类的所有子类必须在相同源文件中定义
七、函数式高级编程
7.1 偏函数
// 匿名内部类形式定义偏函数,表示接受的参数是Any,返回值类型是Int,collect支持偏函数,map不支持偏函数
val paritalFum = new ParitalFunction[Any,Int]{
// 如果返回true 会调用 apply方法创建对象,如果为false则过滤掉元素
override def isDefinedAt(x:Any)={
}
// 返回Int类型对象
override def apply(v:Any)={
}
}
// 简化形式,返回类型必须统一,如果不统一返回类型是Any
val list = List(1,2,3).collcet{
case i:Int=>i+1;
case j:Double=>(j+1).toInt;
case _ =>
}
7.2 匿名函数
val fun = (x:Int)=>{
x+1
}
7.3 高阶函数
能够 接收函数作为参数的函数或者 能够返回函数的的函数叫做 高阶函数
// 接受函数作为参数
// 接受Double类型的参数返回Int类型参数
def fun(f:Double=>Int,n:Double){
f(n)
}
// 返回函数作为参数,也称为闭包
def minusxy(x:Int){
(y:Int)=>x+y;
}
// 或者直接使用匿名函数返回高阶函数,也称为闭包
val fun = (x:Int)=>{
(y:Int)=>x+y
}
// 闭包就是一个函数与相关引用环境组合成的一个整体
7.4 参数类型推断
参数类型可以推断时,可以省略参数类型
传递函数时,只有单个参数可以省略括号
如果参数的变量(无论=>左侧有多少变量),在=>右侧只出现一次,则可以省略=>以及左侧内容,右侧内容的所有变量用_表示
7.5 函数柯里化
将 接收多个参数的函数转化为接受 单个参数的的函数的过程就叫 柯里化
// 通过柯里化比较字符串大小并且转换成小写
implicit class TestEq(s:String){
def checkEq(str:String)(f:(String,String)=>Boolean):Boolean={
f(s.toLowerCase,str.toLowerCase);
}
}
// 调用
val str = "Hello";
str.checkEq(str)(_.equals(_));
7.6 抽象控制
抽象控制是一类特殊的函数,它的 参数是函数,函数没有输入值也没有返回值
def abstractCtrl(f:=>Unit){
f
}
// 直接将语句块传入到f函数中
abstractCtrl{
// 语句块
}