Scala学习笔记(一)基础

来源:互联网 发布:开机自动还原软件 编辑:程序博客网 时间:2024/05/19 20:56

scala类层次结构

Scala Class Hierarchy
与Java一样,Scala也有7种数值类型Byte、Char、Short、Int、Long、Float、Double,以及1个Boolean类型。与Java不同的是,这些类型是类,Scala并不可以区分基本类型和引用类型。

变量与常量

使用val声明常量,val类似于Java里的final变量。一旦初始化了,val就不能再赋值了。

val msg = "hello scala"msg = "hello spark" // 重复赋值会报错

使用var声明变量,var如同Java里面的非final变量。可以在它生命周期中被多次赋值。

var count = 5count = 10

我们也可以指定类型

val msg : String = "hello"println(msg) // 打印输出
  • 我们定义值或变量的时候不需要给出类型,因为scala有类型推导(type inference)
  • 声明值或变量必须初始化,否则会报错
  • 在变量声明或赋值语句之后不需要使用分号,仅当同一行代码中存在多条语句时才需要

条件表达式

Scala的if/else语法结构与Java一样。不过,在Scala中表达式有值,这个值就是跟在if或else之后的表达式的值。表达式同样都有1个类型。

if ( x > 0) 1 else -1 // 表达式的值为1或-1,表达式类型为Intval s = if (x > 0) 1 else -1 // 将表达式的值赋值给变量if (x > 0) "Any" else -1 // 此处为混合类型。一个为java.lang.String,一个为Int。此时表达式的类型是两个分支类型的公共超类型Anyif (x > 0) 1 // 此时if语句可能没有输出值,返回的是Unit,写做()。可以当做返回一个“无有用值”,Unit相当于Java中的voidif (n > 0) { r = r * n; n -= 1} // 如果在单行写多个语句,需要用分号隔开。与Java不同Scala没有提供++和--操作符,需要使用 += 1和 -= 1

循环

Scala中的while和do循环与Java相同

while (n > 0) {    r = r * n    n -= 1}

Scala中的for循环与Java完全不同

for (i <- 1 to n) { // 让变量i遍历 <- 右边表达式的所有值(包含n)    r = r * i}

遍历字符串或者数组时,通常需要使用0到n-1的区间,这个时候可以使用until方法,until方法返回一个不包含上限的区间

val s = "hello"var sum = 0for (i <- 0 until s.length) { // i最后一个取值是s.length - 1    sum += s(i)}

当我们不需要使用下标时,可以直接遍历字符序列

var sum = 0for (str <- "hello") {    sum += str}

高级for循环和for推导式

可以使用 变量 <- 表达式 的形式提供多个生成器,用分号分割

for (i <- 1 to 3; j <- 1 to 3) print ((10 * i + j) + " ") // 打印 11 12 13 21 22 23 31 32 33

可以在for循环结构中加上”if”开头的Boolean表达式,称为守卫。注意if前面没有分号

for (i <- 1 to 3; j <- 1 to 3 if i != j) print ((10 * i + j) + " ") // 打印 12 13 21 23 31 32

可以使用任意多的定义,引入可以在for循环中使用的变量

for (i <- 1 to 3; from = 4 - i; j <- from to 3) print ((10 * i + j) + " ") // 打印 13 22 23 31 32 33

for循环的循环体以yield开始,则会构造出一个集合,每次迭代生成集合中的一个值,这类循环叫for推导式

for (i <- 1 to 10) yield i % 3 // 生成Vector(1, 2, 0, 1, 2, 0, 1, 2, 0, 1)

for推导式生成的集合与它的第一个生成器是类型兼容的

for (c <- "Hello"; i <- 0 to 1) yield (c + i).toChar // 生成"HIeflmlmop"for (i <- 1 to 1; c <- "Hello") yield (c + i).toChar // 生成Vector('H', 'e', 'l', 'l', 'o', 'I', 'f', 'm', 'm', 'p')

函数

要定义函数,需要给出函数的名称、参数和函数体。
必须给出所有参数的类型,除递归函数外不需要指定返回类型。Scala编译器会通过=右侧的表达式推断出类型并返回

def abs(x : Double) = if (x >= 0) x else -x

如果函数体需要多个表达式完成,可以使用代码块。块中最后一个表达式的值就是函数的返回值

// 返回 r,不需要使用 returndef fac(n : Int) = {    var r =1    for (i <- 1 to n) r = r * i    r}

递归函数,必须要指定返回类型

def fac(n : Int) : Int = if (n <= 0) 1 else n * fac(n - 1)

在调用某些函数时并不显式地给出所有参数值,对于这些函数可以使用默认参数

// 这个函数的 left 和 right 带有默认值 "[" 和 "]"def decorate(str : String, left : String = "[", right : String = "]") = left + str + right// 调用函数decorate("Hello") // 得到"[Hello]"// 可以替换掉默认值decorate("Hello", "{", "}") // 得到"{Hello}"// 给出的值比参数数量少,值会从左到右应用进去,剩余的使用默认参数decorate("Hello", "{") // 得到"{Hello]"// 可以在提供参数值得时候指定参数名,带名参数不需要和参数列表顺序一致decorate(left = "{", str = "Hello", right="}") // 得到"{Hello}"

变长参数列表的函数,函数得到的是一个类型为Seq的参数(集合中讲Seq),可以使用for循环来访问每个元素

def sum(args : Int*) = {    var result = 0    for (arg <- args) result += arg    result}// 调用函数val s = sum(1, 3, 5, 7, 9)// 值的序列不能直接传入上述函数val s = sum(1 to 5) // 错误// 解决上面问题的办法是告诉编译器希望这个参数(值的序列)被当做参数序列处理,追加 : _*val s = sum(1 to 5 : _*)// 在递归定义中会用到上述语法// 序列的head是它的首个元素,tail是除了首个元素外的其他所有元素,这又是1个Seq,使用 : _* 将它转换为参数序列def recursiveSum(args : Int*) : = {    if (args.length == 0) 0    else args.head + recursiveSum(args.tail : _*)}

Scala对于不返回值得函数有特殊的表示法,如果函数体包含在花括号当中但没有前面的 “=” 号,那么返回类型就是Unit,这样的函数被称为过程(procedure)。过程不返回值,我们调用它仅仅是为了它的副作用。

def box(s : String) { // 没有 "=" 号    var border = "-" * s.length + "--\n"    println(border + "|" + s + "|\n" + border)}// 也可以显式的声明Unit返回类型def box(s : String) : Unit = {}

懒值

当val被声明为lazy时,它的初始化将被推迟,直到首次对它取值。可以把懒值当做是介于val和def的中间状态

val words = scala.io.Source.fromFile("D:\\data\\words.txt").mkString // 在words被定义时即被取值lazy val words = scala.io.Source.fromFile("D:\\data\\words.txt").mkString // 在words被首次使用时取值def words = scala.io.Source.fromFile("D:\\data\\words.txt").mkString // 在每一次words被使用时取值

每次访问懒值,都会有一个方法被调用,这个方法会以线程安全的方式检查该值是否被初始化

异常

Scala异常的工作机制和Java一样。当抛出异常时,当前运算被终止,运行时系统查找可以接受异常的异常处理器。如果没有找到符合要求的异常处理器,则程序退出。
和Java一样,抛出的对象必须是java.lang.Throwable的子类。不过,与Java不同的是,Scala没有“受检”异常—不需要声明说函数或方法可能抛出某种异常。

在Java中,”受检”异常在编译期被检查。如果方法可能会抛出IOException,就必须做出声明。

throw表达式有特殊的类型Nothing。这在if/else表达式中很有用,如果一个分支的类型是Nothing,那么if/else表达式的类型就是另一个分支的类型。如下,第一个分支类型是Double,第二个分支类型是Nothing。因此,if/else表达式的类型是Double。

if (x >= 0) {    sqrt(x)}else {    throw new IllegalArgumentException("x should not be negative")}

捕获异常的语法采用的是模式匹配的语法,和Java一样更通用的异常应该排在更具体的异常之后。如果不需要使用捕获的异常对象,可以使用 _ 来替代变量名。finally语句与Java是一样的。

var in = new URL("http://img.my.csdn.net/uploads/201606/16/1466068921_7376.png").openStream()try {    process(in)} catch {    case _: MalformedURLException => println("MalformedURLException")    case ex : IOException => ex.printStackTrace()} finally {    in.close()}
0 0
原创粉丝点击