Scala 基础语法
Scala是多范式的编程语言,本教程着重介绍函数式编程.
本教程适用于已经掌握了Java编程语言的人.
常量和变量
var可以用来声明一个变量:
var var_name[:type] = xxx
其中,属性的类型声明可以省略.那么scala会自动推测属性的类型.即使类型可以省略,scala和python不一样,还是有类型的区别的.
Scala官方不建议定义过多变量,最好不要定义任何变量(所有变量的效果通过函数完成).
val可以用来声明一个常量:
val val_name[:type] = xxx
常量不可以被修改.
基本类型
Scala提供以下的基本数据类型:
类型 | 说明 |
---|---|
Byte | 字节类型 |
Short | 短整型 |
Int | 整型 |
Long | 长整型 |
Char | 字符 |
String | 字符串 |
Float | 浮点型 |
Double | 双精度 |
Boolean | 布尔 |
除了String,位于java.lang中,其余的基本类型都处于scala的包下.Scala没有所有真正基本类型的概念,实际上全部都是封装类.但是这些基本类型可以直接在scala中赋值(不需要通过new来赋值).
scala和java.lang包会自动被scala引入,所以使用这些类不需要任何声明.
例如:
val flag = true
// 等价于
val flag: Boolean = true
在Scala中,String支持三引号的写法.”“”xxx”“”这样可以支持原生的字符串,在三引号中的字符串会被直接当作原生字符串处理,不需要任何转义.
Scala为基本类型提供了富包装类RichXxx.并且可以实现自动转换.
操作符
Scala中,操作符即方法,方法即操作符.scala中没有真正意义上的操作符,操作符实际上可以通过函数的方式调用:
// 以下两种写法是一样的:
val num1 = 2 + 3
val num2 = 2.+(3)
字面量2作为对象存在,对象中有+方法.
对于一个方法,可以用操作符的方法去调用:
// 以下两种写法一样:
val str1 = "123456".charAt(1)
val str2 = "123456" charAt 1
Scala没有真正意义上的操作符,所有的操作符都会被转换为方法的调用.
如果方法接收1个以上的参数,括号不能被省略,如果没有参数,可以省略点和括号:
val str1 = "123456".substring(2, 3)
val str2 = "123456" substring (2, 3)
val str3 = "a".toUpperCase()
val str4 = "a" toUpperCase
Scala中方法是有优先级的.在表达式中体现为操作符的优先级.对于一连串的方法调用,优先级高的方法会先得到调用.
优先级是按照方法的名字确定的,是按照方法的名称的第一个字符确定的,以下字符越靠前则优先级越高:
- * / %
- + -
- :
- = !
- <>
- &
- ^
- |
- 所有字母
- 所有赋值操作符
可以通过括号改变优先级,对于优先级相同的方法,是按照从左到右来调用的.
操作符分为三类:
- 中缀操作符,操作符在两个操作数之间
- 后缀操作符,操作符位于一个唯一的操作数的后面
- 前缀操作符,操作符位于一个唯一的操作数的前面,Scala只提供四种: -, +, ~, %.前缀操作符会被翻译为”unary_x”,例如”-3”会被翻译为”3.unary_-()”,前缀操作符我们无法自己定义.
特殊的,以”:”字符为结尾的方法,是右操作数调用,传入左操作数.例如”a::b”会被翻译为”(b).::(a)”
内建控制结构
if可以用作判断,用法和java基本一样.但是scala中if有返回值,if在执行完毕后会将最后一个表达式的值给返回,例如:
val num = 10
val res = if (num > 100) {
"num大于100"
}
else if (num < 100 && num > 5) {
"num在5到100之间"
}
else {
"num小于5"
}
println(res)
res的结果应该是”num在5到100之间”.if实际上是一种函数,不在函数中进行打印满足函数式编程的理念.
while和do…while可以用于循环,和java用法基本一样.scala中的while的返回值是Unit,表示没有返回值.while需要用到变量,并且可能产生额外的影响,不符合函数式编程的思想,所以官方不建议使用while编写循环,建议使用递归替换.
例如,使用递归实现1到100的计算:
def sum100(x: Int, sum: Int): Int = {
if (x <= 100) {
sum100(x + 1, sum + x)
}
else {
sum
}
}
val res = sum100(1, 0)
println(res)
所有的循环控制都应该被写成这样的递归形式.
for的使用和java也是类似的.可以使用for来遍历集合:
val list = List(1, 3, 4, 5, 12)
for (x:Int <- list) {
println(x)
}
to运算符用于生成一个范围的数组,可以直接用于一定范围的循环:
for (x <- 1 to 5) {
println(x)
}
for还可以实现过滤,例如可以通过下面代码遍历0-100的偶数:
for (x <- 0 to 100; if x % 2 == 0) {
println(x)
}
使用多个”;”可以实现多个条件的过滤:
for (x <- 0 to 100; if x > 20; if x < 80) {
println(x)
}
使用for可以实现嵌套循环.
for (i <- 1 to 9; j <- 1 to i) {
print(i * j + ",")
}
这打印了10以内的所有乘法的结果.
for还支持流间变量定义,可以在多循环中定义一个共享的变量.流间变量支持使用if进行赋值,例如通过下面的代码打印完整的九九乘法表:
for (i <- 1 to 9; j <- 1 to i; next = if (i == j) "\n" else "\t") {
print(i + "*" + j + "=" + i * j + next)
}
for的返回值也是Unit.但是yield可以让for把每次循环最后一次返回值组装为一个集合返回.所以上面的代码可以转换为:
val res = for (i <- 1 to 9; j <- 1 to i; next = if (i == j) "\n" else "\t") yield {
i + "*" + j + "=" + i * j + next
}
println(res)
这就满足了函数式编程的要求.
scala继承了java的异常机制,通过throw关键字可以抛出异常,通过try…catch…finally可以捕获异常.
catch的写法和java不同,为:
try {
...
} catch {
case e: Exception1 => ...
case e: Exception2 => ...
...
} finally {
...
}
try可以带有返回值.例如:
val res = try {
...
"no exception"
} catch {
case e: Exception => "have an exception"
}
println(res)
注意如果在finally中返回,这个返回值不会对try和catch的返回值产生影响,这点和java不一样(java的话无论如何都会返回finally的返回值).
match … case类似于java的switch … case.下面是示例:
val str = "abc"
val res = str match {
case "abc" => "我是abc"
case "bbb" => "我是bbb"
case _ => "我既不是abc也不是bbb"
}