一、Scala简介
scala 是一门以 jvm 为运行环境的静态类型编程语言,具备面向对象及函数式编程的特性。
特性:
1.java和Scala可以混编
2.类型推测(自动推测类型)
3.并发和分布式(Actor)
4.特质,特征(类似 java 中 interfaces 和 abstract 结合)
5.模式匹配(类似 java 中的 switch...case )
6.高阶函数
Scala应用场景
kafka:分布式消息队列,内部代码经常用来处理并发的问题,用scala可以大大简化其代码。
spark:方便处理多线程场景,另外 spark 主要用作内存计算,经常要用来实现复杂的算法。利用scala 这种函数式编程语言可以大大简化代码。
二、Scala基础
2.1 标识符
区分大小写
Scala是大小写敏感的
类名(驼峰规则)
对于所有的类名的第一个字母要大写。
如果需要使用几个单词来构成一个类的名称,每个单词的第一个字母要大写。
方法名称
所有的方法名称的第一个字母要小写
如果若干个单词被用于构成方法的名称,则每个单词的第一个字母应大写。
程序文件名
程序文件的名称应该与对象名称完全匹配(新版本不需要了,但建议保留这种习惯)。
保存文件时,应该保存它使用的对象名称(记住Scala是区分大小写),并追加".scala"为文件扩展名
关键字
2.2 数据类型
Scala的数据类型都是对象,也就是说scala没有java中的原生类型。在scala是可以对数字等基础类型调用方法的。
比较特殊的 None ,是 Option 的两个子类之一,另一个是 Some ,用于安全的函数返回值。
scala推荐在可能返回空的方法使用 Option[X] 作为返回类型。如果有值就返回 Some[X] ,否则返回none
Nil 表示 长度为0的List 。
2.4 变量和常量
注意以下俩点
定义变量或者常量的时候,也可以写上返回的类型,一般省略,如: val a:Int = 10
var VariableName : DataType [= Initial Value]
val VariableName : DataType [= Initial Value]
变量:在程序运行过程中其值可能发生改变的量叫做变量。
常量:在程序运行过程中其值不会发生变化的量叫做常量。
在Scala 中声明变量和常量不一定要指明数据类型,在没有指明数据类型的情况下,其数据类型是通过变量或常量的初始值推断出来的。
在没有指明数据类型的情况下声明变量或常量必须要给出其初始值,否则将会报错。
2.5 运算符
算术运算符
关系运算符
逻辑运算符
位运算符
赋值运算符
2.6 逻辑运算符
Scala IF...ELSE 语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块。 同 java 一样的语法结构,单,双,多分支这样三种顺序控制结构。
2.7 循环语句
循环语句允许我们多次执行一个语句或语句组
区间的获取
普通for循环
多层for循环
判断for循环
for循环与yield
while循环
2.8 方法与函数
2.8.1 方法的定义
scala 使用 def 关键字告诉编译器这是一个方法。
返回值类型与返回值
在参数后面加一个冒号和类型来显式地指定返回类型。方法可以写返回值的类型也可以不写,会自动推断,但是如果有显式的Return有时候不能省 略,必须写, Scala 中函数有返回值时,可以写 return ,也可以不写 return ,不写 return 时会把函数 中最后一行当做结果返回。 如果去掉方法体前面的等号,那么这个方法返回类型必定是 Unit 的。scala可以把任意类型转 换为 Unit 。 假设,函数里面的逻辑最后返回了一个 string ,那么这个返回值会被转换成 Unit ,原本逻辑的值会被丢弃。 如果返回值可以一行搞定,可以将 {} 省略不写 scala 规定方法的传过来的参数为 val 的,不是 var 的。
2.8.2 函数的定义
Scala的函数是基于Function家族,0-22,一共23个Function Trait可以被使用,数字代表了 Funtcion的入参个数
Java里只有方法都能适应一切需求,那scala又提出函数的概念肯定有意义。java里只有方法都能适应一切需求,那scala又提出函数的概念肯定有意义。闭包(closure),可以把灵活操作代码块,从而引申出其他灵活的语法
2.8.3 方法与函数
Scala中:在类中定义的函数就是方法,函数可以作为一个参数传入到方法中,而方法就不行,在scala中无法直接操作方法,如果要操作方法,必须先将其转换成函数,通常情况下,编译器会自动将方法编译成函数 var f = m _ //表示将m 方法转换为函数,在需要函数的地方,如果传递一个方法,会自动进行ETA展开(把方法转换为函数)
2.8.4 闭包
闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。
闭包通常来讲可以简单的认为是可以访问一个函数里面局部变量的另外一个函数。
2.8.5 字符串
在Scala 中,字符串的类型实际上是 Java String,它本身没有 String 类。
在Scala 中,String 是一个不可变的对象,所以该对象不可被修改。这就意味着你如果修改字符串 就会产生一个新的字符串对象。
在Scala 中,可以使用 String Builder 类创建一个可以修改的字符串。
2.8.6 数组
Scala 语言中提供的数组是用来存储固定大小的同类型元素,数组对于每一门编辑应语言来说都是重要的数据结构之一。
数组的第一个元素索引为0,最后一个元素的索引为元素总数减1。
创建数组的两种方式:
数组遍历方式(for-foreach)
创建二维数组
可变长数组
数组的复杂应用
2.9 类和对象
2.9.1 构造器
每个类都有一个主要的构造器,这个构造器不是单独声明的构造函数,而是和类定义交织在一起。当你阅读一个Scala类时,你需要将它们分开理解,一个是类的定义,一个是构造函数的定义。 除了主构造器之外,类还可以有任意多的辅助构造器(auxiliary constructor)。辅助构造器的名称为 this 每一个辅助构造器都必须以一个对先前已定义的其他辅助构造器或主构造器的调用开始
2.9.2 属性
类的所有属性默认都会被私有化,但是会生成getter和setter方法
如果属性用private修饰,生成getter和setter方法也是私有的
类的属性如果用val修饰,默认拥有getter方法
可以使用@BooleanBeanProperty注解增加Java方式的getter和setter
2.9.3 嵌套类
在Scala中,你几乎可以在任何语法结构中内嵌任何语法结构。你可以在函数中定义函数,在类中 定义类。
在Scala中,每个实例都有它自己的Member类,就和它们有自己的members字段一样
要构建一个新的内部对象,你只需要简单的new这个类名:new chatter.Member。
2.10 伴生类和伴生对象
Object的由来
在Scala 中,是没有 static 这个东西的,但是它也为我们提供了单例模式的实现方法,那就是使用Object关键字
Scala中使用单例模式时,除了定义的类之外,还要定义一个同名的 object 对象,它和类的区别是,object对象不能带参数。
当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象:companion object
在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class
类和它的伴生对象可以互相访问其私有成员。
使用object 时,不用 new ,使用 class 时要 new ,并且 new 的时候, class 中除了方法不执行,其他都执行。
scala中的 object 是单例对象,相当于 java 中的工具类,可以看成是定义静态的方法的类。object 不可以传参数。
2.11 继承
Scala 使用 extends 关键字来继承一个类。Scala 只允许继承一个父类。重写一个非抽象方法必须使用override修饰符。 只有主构造函数才可以往基类的构造函数里写参数。在子类中重写超类的抽象方法时,你不需要使用override关键字。 Scala 使用 extends 关键字来继承一个类。实例中 Location 类继承了 Point 类。Point 称为父类(基类),Location 称为子类。
override val xc 为重写了父类的字段。继承会继承父类的所有属性和方法,Scala 只允许继承一个父类。
2.12 trait
Scala 中 Trait (特征) 相当于 Java 的接口,实际上它比接口还功能强大。 与接口不同的是,它还可以定义属性和方法的实现。一般情况下Scala的类可以继承多个 Trait ,从结果来看就是实现了多重继承。 Trait (特征) 定义的方式与类类似,但它使用的关键字是 trait 。
2.12.1 trait中带属性的方法实现
以上Trait(特征)由两个方法组成:isEqual 和 isNotEqual。isEqual 方法没有定义方法的实现,isNotEqual定义了方法的实现。子类继承特征可以实现未被实现的方法。所以其实 Scala Trait(特征)更像 Java 的抽象类。
2.12.2 特征构造顺序
特征也可以有构造器,由字段的初始化和其他特征体中的语句构成。这些语句在任何混入该特征的对象在构造时都会被执行。
构造器的执行顺序
调用超类的构造器
特征构造器在超类构造器之后、类构造器之前执行;
特征由左到右被构造;
每个特征当中,父特征先被构造;
如果多个特征共有一个父特征,父特征不会被重复构造
所有特征被构造完毕,子类被构造
构造器的顺序是类的线性化的反向。线性化是描述某个类型的所有超类型的一种技术规格。
2.13 修饰符
Scala 访问修饰符基本和Java的一样,分别有:private,protected,public。
如果cala 访问修饰符基本和Java的一样,分别有:private,protected,public。
Scala 中的 private 限定符,比 Java 更严格,在嵌套类情况下,外层类甚至不能访问被嵌套类的私有成员。
2.13.1 private
用private 关键字修饰,带有此标记的成员仅在包含了成员定义的类或对象内部可见,同样的规则还适用内部类。
(new Inner).f( ) 访问不合法是因为 f 在 Inner 中被声明为 private,而访问不在类 Inner 之内。在 InnerMost 里访问 f 就没有问题的,因为这个访问包含在 Inner 类之内。Java 中允许这两种访问,因为它允许外部类访问内部类的私有成员。
2.13.2 Protected
在scala 中,对保护(Protected)成员的访问比 java 更严格一些。因为它只允许保护成员在定义了该成员的的类的子类中被访问。而在java中,用 protected关键字修饰的成员,除了定义了该成员的类的子类可以访问,同一个包里的其他类也可以进行访问。
上例中,Sub 类对 f 的访问没有问题,因为 f 在 Super 中被声明为 protected,而 Sub 是 Super 的子类。相反,Other 对 f 的访问不被允许,因为other 没有继承自 Super。而后者在 java 里同样被认可,因为 Other 与 Sub 在同一包里。
2.13.3 public
在Scala 中,如果没有指定任何的修饰符,则默认为 public。这样的成员在任何地方都可以被访问。
2.13.4 作用域保护
在Scala中,访问修饰符可以通过使用限定词强调。格式为:
这里的x指代某个所属的包、类或单例对象。如果写成private[x],读作"这个成员除了对[…]中的类或[…]中的包中的类及它们的伴生对像可见外,对其它所有类都是private。这种技巧在横跨了若干包的大型项目中非常有用,它允许你定义一些在你项目的若干子包中可见但对于项目外部的客户却始终不可见的东西。
上述例子中,类 Navigator 被标记为 private[bobsrockets] 就是说这个类对包含在 bobsrockets 包里的所有的类和对象可见。比如说,从 Vehicle 对象里对 Navigator 的访问是被允许的,因为对象 Vehicle 包含在包 launch 中,而 launch 包在 bobsrockets 中,相反,所有在包 bobsrockets 之外的代码都不能访问类 Navigator。
三、参数传递
Scala的解释器在解析函数参数(function arguments)时有两种方式: 传值调用(call-by-value):先计算参数表达式的值,再应用到函数内部;
Call-by-Value 避免了参数的重复求值,效率相对较高
传名调用(call-by-name):将未计算的参数表达式直接应用到函数内部
Call-by-Name 避免了在函数调用时刻的参数求值,而将求值推延至实际调用点,但有可能造成重复的表达式求值。
四、Scala函数深化
4.1 指定参数名
一般情况下函数调用参数,就按照函数定义时的参数顺序一个个传递。但是我们也可以通过指定函数参数名,并且不需要按照顺序向函数传递参数
4.2 可变参数
Scala 允许你指明函数的最后一个参数可以是重复的,即我们不需要指定函数参数的个数,可以向函数传入可变长度参数列表。
Scala 通过在参数的类型之后放一个星号来设置可变参数(可重复的参数)。
4.3 递归函数
自己调用自己
必须有程序的出口
4.4 默认参数值
Scala 可以为函数参数指定默认参数值,使用了默认参数,调用函数的过程中如果没有传递参数,函数就会调用它的默认参数值, 调用函数的过程中如果传递了参数,则传递值会取代默认值。
4.5 匿名函数
Scala 中定义匿名函数的语法很简单,箭头左边是参数列表,右边是函数体。
1. 有参匿名函数
2.无参匿名函数
3.有返回值的匿名函数
可以将匿名函数返回给val定义的值
匿名函数不能显式声明函数的返回类型
4.6 嵌套函数
可以在 Scala 函数内定义函数,定义在函数内的函数称之为局部函数。
4.7 偏应用函数
Scala 偏应用函数是一种表达式,你不需要提供函数需要的所有参数,只需要提供部分,或不提供所需参数。
例如:一个参数是完全相同,另一个参数不同,在这样一个情况之下,我们可以使用偏应用函数来进行优化。
4.8 高阶函数
高阶函数(Higher-Order Function)就是操作其他函数的函数。
Scala中允许使用高阶函数, 高阶函数可以使用其他函数作为参数,或者使用函数作为输出结果。
函数的参数是函数
函数的返回是函数
函数的参数和函数的返回是函数
五、Scala集合深化
scala.collection 中的所有集合。这些都是高级抽象类或特征,通常具有可变和不可变的实现。
scala.collection.immutable中的所有集合。不变的
不会被改变。但仍可以进行模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变。
scala.collection.mutable中的所有集合。可变的,可以在适当的地方被更新或扩展,可以修改,添加,移除一个集合的元素。
5.1.1 Trait Traversable
Traversable(遍历)是容器(collection)类的最高级别特性(trait),它唯一的抽象操作是foreach:实现Traversable的容器(collection)类仅仅需要定义与之相关的方法,其他所有方法可都可以从 Traversable中继承。
5.1.2 Trait Iterable
容器(collection)结构的上层还有另一个trait。这个trait里所有方法的定义都基于一个抽象方法, 迭代器(iterator,会逐一的产生集合的所有元素)。foreach是Traversable所有操作的基础,所以它的性能表现很关键。Iterable有两个方法返回迭代器:grouped和sliding。这些迭代器返回的不是单个元素,而是原容 器(collection)元素的全部子序列。 grouped方法返回元素的增量分块 sliding方法生成一个滑动元素的窗口。
5.1.3 Trait Seq
Seq trait用于表示序列。序列,指的是一类具有一定长度的可迭代访问的对象,其中每个元素均带有一个从0开始计数的固定索引位置。
trait Seq 具有两个subtrait LinearSeq和IndexedSeq。线性序列具有高效的 head 和 tail 操作 索引序列具有高效的apply, length, 和 (如果可变) update操作。 Vector 类提供一个在索引访问和线性访问之间有趣的折中。它同时具有高效的恒定时间的索引开销,和恒定时间的线性访问开销。
5.1.4 Trait Set
集合是不包含重复元素的可迭代对象。
可变集合和不可变集合提供了+和++操作符来添加元素,-和--用来删除元素。但是这些操作在可变集合中通常很少使用,因为这些操作都要通过集合的拷贝来实现。更有效率的方法是+=和-=,另外 还有批量操作符++=和--=。
目前可变集合默认使用哈希表来存储集合元素,非可变集合则根据元素个数的不同,使用不同的方式来实现。空集用单例对象来表示。元素个数小于等于4的集合可以使用单例对象来表达,元素作为单例对象的字段来存储。元素超过4个,非可变集合就用哈希前缀树(hash trie)来实现。采用这种表示方法,较小的不可变集合(元素数不超过4)往往会比可变集合更加紧凑和高效。所以,在处理小尺寸的集合时,不妨试试不可变集合。
集合的两个特质是 SortedSet 和 BitSet.
SortedSet 是指以特定的顺序(可以在创建集合时设置顺序)排列其元素(使用iterator或foreach)的集合,默认表示是有序二叉树,即左子树上的元素小于所有右子树上的元素。这 样,一次简单的顺序遍历能按增序返回集合中的所有元素。Scala的类 immutable.TreeSet 使 用红黑树实现,它在维护元素顺序的同时,也会保证二叉树的平衡,即叶节点的深度差最多为1。
BitSet是由单字或多字的紧凑位实现的非负整数的集合。其内部使用 Long 型数组来表示。第 一个 Long 元素表示的范围为0到63,第二个范围为64到127,以此类推(值为0到127的非可变位集合通过直接将值存储到第一个或第两个 Long 字段的方式,优化掉了数组处理的消耗)。对于每个 Long,如果有相应的值包含于集合中则它对应的位设置为1,否则该位为0。位集合的大小取决于存储在该集合的最大整数的值的大小。假如N是为集合所要表示的最大整数,则集合的大小就是 N/64 个长整形字,或者 N/8 个字节,再加上少量额外的状态信息字节。当位集合包含的元素值都比较小时,它比其他的集合类型更紧凑。位集合的另一个优点是它的 contains 方法(成员测试)、+= 运算(添加元素)、-= 运算(删除元素)都非常的高效。
5.1.4 Trait Map
Map是一种可迭代的键值对结构。Scala的Predef类提供了隐式转换,允许使用另一种语法:key ->value 代替(key,value)
5.2 list
Scala 列表类似于数组,它们所有元素的类型都相同,但是它们也有所不同:列表是不可变的,值一旦被定义了就不能改变,列表具有递归的结构
构造列表的两个基本单位是 Nil 和 :: 不要忘记 Nil 是 长度为0的List 。
Scala列表有三个基本操作:head 返回列表第一个元素,tail 返回一个列表,包含除了第一元素之外的其他元素,isEmpty 在列表为空时返回true
5.2.1 list遍历
for,foreach俩种
5.2.2 list方法举例
filter :过滤元素,count :计算符合条件的元素个数,map :对元素操作,flatmap :压扁扁平,先map再flat
5.2.3 可变长度的list
5.3 Set
Scala Set(集合)是没有重复的对象集合,所有的元素都是唯一的。 Scala 集合分为可变的和不可变的集合。 默认情况下,Scala 使用的是不可变集合,如果你想使用可变集合,需要引用 scala.collection.mutable.Set 包。 Scala集合有三个基本操作:
head返回集合第一个元素
tail返回一个集合,包含除了第一元素之外的其他元素
isEmpty在集合为空时返回true