1 基础
变量声明和赋值
在Scala 中 Val 定义一个常量, 相当于C /C++ 中的Const, 一旦定义,其值不可改变
val answer = 0
如果要声明可变得变量 用Var , 在Scala 中鼓励使用 val, 大多数程序并不需要那么多var 变量
var answer = 0
answer = 1 //OK
Notice: 你不需要指定变量的类型,Scala会推断出它的类型, 不过可以在需要的时候声明出它的类型
// 声明方式不同于C++/Java
val answer : Int = 12
var geeting : String = "Hello World"
另外不同于Java/C++, 每行结束不需要; 仅当一行存在多条语句时才需要 用; 隔开
常用类型
Scala 中有七种数值类型 Byte Char Short Long Int Float Double 和一个Bolean 类型 和Java/C#不同, 这些类型都是类, 在Scala中并刻意区分引用类型和数值类型
对数字执行方法
1.toString() //产生字符串“1”
实际上 Int 并不具备to 方法, 而是RIchInt 对其扩展
在scala中总是用方法表示数值间的转换,而不是强制类型转换
操作符重载
Scala的算术操作符和Java/C++ 的预期效果是一样的,但是特别的是这些操作都是方法,eg:
val answer = 8 * 5 + 2
其实是 (8. *5). + 2简写, 根据自己的喜好可以选择其中一种使用
但是和C++/Java 不同的是 在Scala 中没有提供++ 和 -- 操作,当需要时使用+=1 或者 -=1, Scala拒绝提供++ 操作符的原因是你无法简单实现一个++ 的方法, 因为Int是不可变的,这样一个方法无法改变一个整数类型的值。
Notice:在Java中你无法对操作符进行进行重载,防止大家造出!@&*这样的操作符,降低程序的可读性。Scala 允许你定义操作符,由你决定分寸使用这个特性。
调用函数和方法
Scala除了方法外,还提供了函数。 相比其他语言,Scala使用函数更为简单, 不需要从每个类中调用它的静态方法。使用方法如下:
import scala.math._ //在Scala中, _是通配符
sqrt(2) //get 1.41421356
power(2,4) //get 16.0
Notice: 在使用以Scala 开头的Package时, 可以省略Scala前缀,如import math._ 等价import scala.math._
一般不带参数且不改变当前对象的Scala 方法不使用圆括号, 例如: StringOps类distinct方法不带圆括号
Apply方法
在scala中我们经常使用类似函数调用的语法, 如果S是一个字符串, S(i)就是该字符串的第I+1个字符
“Hello”(4) //get 'o'
可以将这中用法当作()的操作符重载,背后实现的原理是一个名为Apply的方法
def apply( n: Int) : Char
Apply方法也是Scala中构建对象的常用手法,例如调用
BigInt("1234567890") //BigInt.apply("1234567890")
Array(1,4,9,16) //返回一个数组,用的也是Array伴生对象的Apply方法
2 控制结构和函数
条件表达式
和Java 和C++的语法结构一样,不同的是Scala的条件表达式有一个返回值,例如
val result = if (x > 0) 1 else -1
C++ 和 Java 有一个 ?:的操作符用于同样的目的, 但是不能再?:表达式中插入语句, Scala的 if else 将C++/Java 的 If else 和 ?:集合在一起了
在Scala中每个表达式都有一个类型, if else 两个分支都有类型, 上例中表达式的类型是Int ,因为两个分支的类型都是Int,如果是混合类型的条件表达式
if x > 0 "positive" else -1
此表达式返回的是他们的公超类型Any
如果else 部分丢失了,该如何呢?在Scala中每个表达式都有某种值,这个问题的解决是引入一个Unit类,写作(), 相当于C++/Java中的void
Notice:在Scala 中没有Switch,但是有强大的多模式匹配机制
语句终止
不同于C++Java,Scala 不需要分号结束, 同样在},else 以及其他类似的位置也不需要分号结束,只要能从上文明确的判断出语句的终止即可,不过想要在单行中写多个语句就需要;隔开
if( n > 0){ r = r * n; n = n -1}
对于过长的语句,需要分两行来写的话就要确保一个不能用来做语句结尾的字符来结尾, 通常是选择一个操作符
s = s0 + (v- v0) * t + //+告诉编译器这不是语句的结束
0.5 * ( a - a0) * t + t
块表达式和赋值
在Scala中,{ }块是一系列表达式,最后一个表达式的值就是块表达式的值
val distance = {val dx = x1 - x2; val dy = y1 - y2; sqrt(dx * dx + dy * dy)//块表达式的值 }
赋值操作是没有值得, 更严格的将它的返回值是Unit,这个类型只有一个值(), 赋值语句的返回值是(),所有最好别把他们串一起
x = y = 1 // don't do like this
循环
Scala拥有和Java/C++相同的While 和 do 循环 ,但是没有对应的for循环, 如果需要这样的for 循环,使用如下
for( i <- 1 to n)
r = r * i
这个循环的结构是 for(i <- 表达式), 让变量 i遍历 <-右边表达式所有的值
Notice: 在for变量循环之前, i 并未被指定为val 或者var , 变量的类型是集合元素的类型, 作用域一直持续到循环结束
在遍历数组的时候, 通常使用从0 到 n-1的区间, 使用util方法代替to方法, util返回一个不包含上限的区间
val s = "hello"
var sum = 0
for(i <- 0 until s.length)
sum += s(i)
在Scala 中, 循环使用的频率不如其他语言, 通常我们对序列中的所有值应用某个方法来处理
Scala并没有提供Break 和 Continue语句来退出循环,控制权的转移时通过抛出和捕获异常来实现的, 为了效率尽量避免使用这套机制
Scala 循环提供了其他语言不具备的高级特性,可以使用 变量 <- 表达式 提供多个生成器;中间用,隔开
for(i <- 1 to 3; j <- 1 to 3) print((10*i + j) + " ") // result 11 12 13 12 22 23 31 32 33
同时每个生成器都可以带一个卫士
for(i <- 1 to 3; j <- 1 to 3 if i != j) print((10*i + j) + " ") // result 12 13 12 23 31 32
//注意在If之前没有分号
函数和过程
Scala 除了支持方法还支持函数, 方法对对象进行操作而函数不是。 C++有函数,但是Java只能用静态方法来模拟。
函数包含 函数名, 参数, 函数体,举个例子
def abs(x: Double) = if(x > 0) x else -x
你必须给出每个参数的类型,但是如果函数不是递归的就不需要给出函数的返回值类型,scala可以自行推断出来, 函数体如果是多个语句需要用{}表达式
递归的函数必须给出返回值, 如果没有返回值就无法校验 n * fac(n-1)的返回值是否为Int
def fac( n : Int ) : Int = {
if n <= 0 1 else n* fac( n-1)
}
我们在调用某些函数时并不显示的给出所有参数的值,对于这些函数我们使用默认参数
def decorate(str: String, left : String = "[", right: String = ']') = left + str + right
decorate("Hello") // show "Hello"
你也可以在提供参数值的时候指定参数名, 注意带名参数并不需要跟参数列表的顺序完全一致
<pre class="plain" name="code">decorate(left = "<<<", str = "hello", right = ">>>")
Scala 也支持变长参数, 你可以用任意多的参数调用此函数
<span style="font-family:Courier New;background-color: rgb(240, 240, 240);">def sum(args: Int*) ={
var result = 0
for(arg <- args) result += arg
result
}</span>
val s = sum(1, 4, 9, 16), 函数得到是一个类型为Seq的参数,如果你已经有一个值序列,则不能直接将它传人上述函数
var s = sum(1 to 5) // error
解决此问题的方法是告诉编译器希望这个参数当作参数序列处理, 追加:_*
val s = sum(1 to 5: _* )
在递归定义中我们会用到上述的语法
def recursiveSum(args: Int*): Int = {
if(args.length == 0) 0
else args.head + recursiveSum(args.tail: _*)
}
Scala 中对于不返回值的函数有特殊的表示法,如果函数体包含在花括号内但没有前面的=号,那么返回类型就行Unit,这样的函数被叫做过程, 过程不返回值, 它的调用只是为了它的副作用
当然可以不用简明的方法表示过程
def box(s: String): Unit = {
...
}
懒值和异常
当Va 被声明为lazy时, 它的初始化将被推迟, 直到我们首次对它取值。
lazy val words = scala.io.Source.fromFile("/usr/share/dict/words").mkString
如果程序不访问words,那么文件也不会被打开。
Scala的异常处理机制和Java/C++ 没有多大的区别,只是Scala没有“受检”异常, 你不需要声明说函数或者方法可能会抛出某种异常, 捕获异常的方法是采用模式匹配的方式
try {
process(new URL("http://baidu.com"))
}catch {
case _: MalformedURLException => println("Bad Url: " + url)
case ex: IoException => ex.printStackTrace()
}
更通用的异常排在更具体的异常之后, 如果你不需要使用捕获的异常对象,可以使用_来替代变量名