摘要
走马观花的学习了一下 Scala 的基本语法,以下示例基于 Scala 2.11.12
版本编写。
变量
可变和不可变量
Scala 中变量有两种不同类型:
1)val:不可变,在声明时必须初始化,且之后不能再复制
2)var:可变,声明时需要初始化,之后可以再次赋值
数据类型
scala 可以根据赋值推断数据类型1
val myName = "dcwang"
也可以指定类型1
val myName2 : String = "dcwang"
还可以利用全限定名指定类型,Scala中使用的是Java类型1
val myName3 : java.lang.String = "dcwang"
所有 scala 文件默认会导入 java.lang 下面所有的包,在 scala中表示为:1
import java.lang._
用下划线表示所有
1 | var price = 1.2 |
在 scala 中所有的基本类型都是 类
基本运算
在 scala 中所有运算都是调用函数1
val sum1 = 5 + 3
相当于1
val sum2 = (5).+(3)
富包装类
Range
1 | 1 to 5 |
1 to 5 相当于 1.to(5)1
2scala> 1 until 5
res2: scala.collection.immutable.Range = Range(1, 2, 3, 4)\
1 | scala> 1 to 10 by 2 |
控制结构
判断
1 | val x = 6 |
scala 中可以将 if 中判断的值赋值给变量
while 循环
1 | var i = 9 |
for 循环
1 | for (i <- 1 to 5) |
文件操作
文本文件读写
Scala 需要调用 java.io.PrintWriter 实现文件写入1
2
3
4
5
6
7
8import java.io.PrintWriter
val out = new PrintWriter("output.txt")
for (i <- 1 to 5)
out.println(i)
out.close()
使用 Scala.io.Source 的 getLines 方法实现对文件中所有行的读取1
2
3
4
5
6
7
8import scala.io.Source
val inputFile = Source.fromFile("output.txt")
val lines = inputFile.getLines
for (line <- lines)
println(line)
异常捕获
Scala 不支持 Java 中的 checked exception,将所有异常当做运行时异常
Scala 仍然使用 try-catch 捕获异常
1 | import java.io.FileReader |
容器 Collections
Scala 提供了一套丰富的容器库,包括列表、数组、集合、映射等
Scala 用三个包来组织容器,分别是1
2
3scala.collection
scala.collection.mntable
scala.collection.immutable
列表 List
共享相同类型的不可变的对象序列,定义在 scala.collection.immutable 中
List 在声明时必须初始化1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var strList = List("BigData", "Hadoop", "Spark")
// 返回头部第一个元素
strList.head
// 返回除了第一个之外的其他元素(一个新的List)
strList.tail
// 连接操作(从右侧开始)
var oneList = List(1, 2, 3)
var otherList = List(4, 5)
var newList = oneList::otherList
// Nil 是一个空列表对象
var intList = 1::2::3::Nil
集合 Set
集合中的元素插入式无序的,以哈希方法对元素的值进行组织,便于快速查找
集和包括可变和不可变集和,分别位于 scala.collection.mntable
和 scala.collection.immutable
包中,默认情况下是不可变集和。
1 | var mySet = Set("Hadoop", "Spark") |
变量是可变的,集合不可变,加操作导致生成了一个新的集合。
可变集合,在原集合中增加一个元素。1
2
3
4import scala.collection.mutable.Set
val myMutableSet = Set("BigData", "Spark")
myMutableSet += "Scala"
映射 Map
映射时一系列键值对的容器。也有可变和不可变两个版本,默认情况下是不可变的。
1 | val university = Map("XMU" -> "Xiamen University", "THU" -> "Tsinghua University", "PKU" -> "Peking University") |
可变映射1
2
3
4
5
6import scala.collection.mutable.Map
val university = Map("XMU" -> "Xiamen University", "THU" -> "Tsinghua University", "PKU" -> "Peking University")
university("XMU") = "Ximan University" // update
university("FZU") = "Fuzhou University" // add
遍历映射
1 | for ( (k,v) <- university) { |
迭代器 Iterator
迭代器不是一个集合,而是一种访问集合的方法。
迭代器有两个基本操作:next 和 hasNext。
1 | val iter = Iterator("Hadoop", "Spark", "Scala") |
grouped & sliding
grouped 返回元素的增量分块1
2
3
4
5
6
7
8
9
10
11scala> val xs = List(1,2,3,4,5)
xs: List[Int] = List(1, 2, 3, 4, 5)
scala> val git = xs grouped 3
git: Iterator[List[Int]] = non-empty iterator
scala> git.next()
res0: List[Int] = List(1, 2, 3)
scala> git.next()
res1: List[Int] = List(4, 5)
sliding 生成一个滑动元素的窗口1
2
3
4
5
6
7
8
9
10
11scala> val sit = xs sliding 3
sit: Iterator[List[Int]] = non-empty iterator
scala> sit.next()
res3: List[Int] = List(1, 2, 3)
scala> sit.next()
res4: List[Int] = List(2, 3, 4)
scala> sit.next()
res5: List[Int] = List(3, 4, 5)
数组 Array
是一种可变的、可索引的、元素具有相同类型的数据集合,Scala提供了类似于Java中泛型的机制指定数组类型。也可以不指定类型。
1 | val intValueArr = new Array[Int](3) |
多维数组
定义多维数组使用 ofDim()
方法1
val myMatrix = Array.ofDim[Int](3,4) // 三行四列
访问元素1
myMatrix(0)(1)
不定长数组
1 | import scala.collection.mutable.ArrayBuffer |
元组 Tuple
不同类型的值的集合。1
2
3val tuple = ("BigData", 2015, 45.0)
println(tuple._1)
println(tuple._2)
面向对象
类
基本类结构
简单类1
2
3
4
5
6
7
8
9class Counter {
private var value = 0
def increment(): Unit = {
value += 1
}
def current(): Int = { value }
}
定义方法可以省略返回类型:1
2
3
4
5
6
7
8
9class Counter {
private var value = 0
def increment() {
value += 1
}
def current(): Int = { value }
}
方法传参1
2
3
4
5
6
7
8
9class Counter {
private var value = 0
def increment(step: Int) {
value += step
}
def current(): Int = { value }
}
创建对象1
2
3
4
5val myCounter = new Counter
myCounter.increment()
println(myCounter.current())
val myCounter2 = new Counter()
编译&执行
如下定义的这样一个类在执行时,直接使用 scala 解释器执行即可,不需要编译。1
2
3
4
5
6
7
8
9class Counter {
private var value = 0
def increment() {
value += 1
}
def current(): Int = { value }
}
如果要编译,需要创建一个单例对象:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class Counter {
private var value = 0
def increment() {
value += 1
}
def current(): Int = { value }
}
object MyCounter {
def main(args:Array[String]) {
val myCounter = new Counter
myCounter.increment()
println(myCounter.current)
}
}
编译(后面跟的是文件名):1
scalac counter.scala
编译之后会产生一些文件:1
2
3Counter.class
MyCounter.class
MyCounter$.class
执行
执行时后面跟的是包含 main
方法的对象名称1
scala -classpath . MyCounter
getter & setter
1 | class Person { |
构造器
Scala构造器包含一个主构造器和若干个辅构造器
辅助构造器的名称为 this
,每个辅助构造器都必须调用一个已有的构造器
1 | class Person { |
对象
单例对象
1 | object Person { |
伴生对象
在 Java 中经常用到同时包含实例方法和静态方法的类,在 Scala 中可以用伴生对象来实现
类和它的伴生对象必须在同一个文件中,并且可以相互访问私有成员
当单例对象与某个类具有相同的名称时,它被成为这个类的伴生对象
1 | class Person { |
applay 方法
update 方法
继承
在子类中重写超类抽象方法时不需要使用 override
关键字
重写一个非抽象方法必须使用 override
修饰符
只有主构造器可以调用超类的主构造器
可以重写超类中的子段
抽象类
1 | // 抽象类,不能直接实例化 |
继承抽象类
1 | class BMWCar extends Car { |
特质(trait)
在 Scala 中没有接口的概念,而是提供了 trait,它实现了接口的功能,以及许多其他特性
trait 是 Scala 中代码重用的基本单元,可以同时拥有抽象方法和具体方法
在 Scala 中一个类只能继承一个超类,但是可以实现多个 trait,从而拥有 trait 中的方法和字段,实现多重继承。
1 | trait CarId { |
混入多个trait1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18trait CarId {
var id : Int
def currentId() : Int
}
trait CarGreeting {
def greeting(msg : String) { println(msg) }
}
// 使用 extends 关键字继承 trait
// 后面混入的多个 trait 可以反复使用 with 关键字
class BYDCarId extends CarId with CarGreeting {
override var id = 10000
def currentId() : Int = {
id += 1;
id
}
}
模式匹配
简单匹配
类似于 Java 中的 switch1
2
3
4
5
6
7
8
9
10val colorNum = 1
val colorStr = colorNum match {
case 1 => "red"
case 2 => "green"
case 3 => "yellow"
case _ => "Not Allowed"
}
println(colorStr)
获取匹配值
可以声明一个变量 unexpected
,用于获取进行匹配的值,然后在分支中进行操作。1
2
3
4
5
6
7
8
9
10val colorNum = 4
val colorStr = colorNum match {
case 1 => "red"
case 2 => "green"
case 3 => "yellow"
case unexpected => unexpected + " is Not Allowed"
}
println(colorStr)
类型模式
可以匹配元素的类型,根据类型选择不同操作。1
2
3
4
5
6
7
8
9
10
11for (elem <- List(9, 12.3, "Spark", "Hello")) {
val str = elem match {
case i : Int => i + " is an int value."
case d : Double => d + " is a double value."
case s : String => s + " is a string value."
case "Hello" => "Hello is here."
case _ => "unexpected value"
}
println(str)
}
守卫(guard)语句
将 case 的选项设置为全匹配,然后用 if 判断进行处理。那为什么不直接用判断?
case类的匹配
一次匹配多个值,或者对比对象?1
2
3
4
5
6
7
8
9
10
11
12
13case class Car(brand: String, price: Int)
val myBYDCar = new Car("BYD", 89000)
val myBMWCar = new Car("BMW", 1200000)
val myBenzCar = new Car("Benz", 1500000)
for (car <- List(myBYDCar, myBMWCar, myBenzCar)) {
car match {
case Car("BYD", 89000) => println("BYD")
case Car("BMW", 1200000) => println("BMW")
case Car(brand, price) => println(brand + price)
}
}
Option类型
处理 None
返回值1
2
3
4
5
6
7val someMap = Map("spark" -> 123, "hadoop" -> 234)
var someValue = someMap.get("hive")
println(someValue.getOrElse("No such value")) // No such value
someValue = someMap.get("spark")
println(someValue.getOrElse("No such value")) // 123
Option[T]
类中的 T
可以是各种数据类型,如果一个 Option
对象中包含值,那么这个对象就是 Some
类型,否则就是 None
类型。
如果返回值是一个集合,就可以对其使用 map
、foreach
或 filter
等方法1
someMap.get("spark").foreach(println)
函数式编程
函数定义
函数字面量
每个函数本身是一个值,可以被传递,类似于 JavaScript 中函数的概念。
函数的类型和值
函数的类型是指传入参数和返回值的类型。1
def counter(value: Int): Int = { value += 1 }
上面这个函数的类型就是: (Int) => Int
如果有多个参数,使用逗号隔开;否则括号可以省略。
函数的值是指去掉了参数类型和返回值类型之后剩下的参数和函数体:1
(value) => { value += 1 }
对比声明一个基本类型来声明一个函数:1
val num : Int = 5
基本类型,声明一个变量 num
,指定变量类型为 Int
,然后给变量赋值为 5
。
1 | var counter = (value : Int) => { value + 1 } : Int |
匿名函数、Lamda表达式与闭包
Lamda 表达式1
2
3(参数) => 表达式
(num: Int) => num * 2
闭包
在一个函数内部可以访问外部变量的形式。1
2
3
4
5
6var more = 1
var addMore = (x: Int) => x + more
println(addMore(10))
more = 5
println(addMore(10))
占位符语法
为了让函数字面量更简洁,可以使用下划线作为一个或多个参数的占位符,每个参数仅可以在函数字面量中出现一次。
1 | val numList = List(1, 2, 3, 4, 5) |
针对集合的操作
map
map操作是针对集合的典型变换操作,将函数应用到集合中的每一个元素上,并产生一个新的结果集合。
1 | val books = List("Hadoop", "Hive", "Spark") |
flatMap
调用一个函数,将一个集合中的每个元素处理后形成的多个集合,合并成一个集合。1
2
3val books = List("Hadoop", "Hive", "Spark")
val letters = books.flatMap(s => s.toList)
println(letters) // List(H, a, d, o, o, p, H, i, v, e, S, p, a, r, k)
filter
遍历一个集合,过滤其中的元素形成一个新的集合。
1 | val books = List("Hadoop", "Hive", "Spark") |
reduce
对给定集合中的两两元素,指定某给定函数的操作。1
2
3
4
5
6
7val numList = List(1, 3, 5)
var res = numList.reduceLeft({_ - _})
println(res) // -7
res = numList.reduceRight({_ - _})
println(res) // 3
// 1 - (3 - 5)
fold
带初始值的规约1
2val numList = List(1, 3, 5)
val res = numList.fold(10)({_ * _})