Declaring values and variables
scala推荐使用val声明变量,除非你需要修改变量内容才使用var
scala中变量的类型要写在变量名后,根据stackoverflow上众位大神的解释,好处如下:
- 名称比类型更重要,这样做可以突出强调变量本身,而不是变量的类型。在scala中,类型推断应该交给编译器完成,写代码的人更应该关注如何使用
- 代码写起来更整洁,比如很多行由val,var,def开头的变量声明
- 使用diff来比较的时候,可以很清楚的看到某个变量的类型变化
Java中的Primitive Types都被重新定义成Class实现,要多多注意这些类的使用方法
- Integer - RichInt
- Double - RichDouble
- Char - RichChar
- String - StringOps
Arithmetic and Operator Overloading
Operator实际上都是类的各种方法,比如这个加法
a + b
a.+(b)
注意++操作符是不存在的
Calling Functions and Methods
比如Java中的Math.abs(a), method必须得跟在一个类的后面。到了scala,方法是可以脱离类存在的,就好像其他语言中的build-in functions一样,比如
import scala.math._ sqrt(20) pow(2, 4) min(3, Pi)
scala中没有static method的说法,但是它提供了一种叫singleton objects的类似功能,即class可以有一个companion object来提供static methods (具体的用法后面解释)
如果是没有参数的method,写的时候就不要加括号了,啰嗦。一般的,如果该方法不会对象,那也可以不写括号。
The apply Method
没太看懂这段的介绍,stackoverflow上一个解释
Conditional Expressions
scala中的if/else语句是有返回值的,比如下面这个例子。很明显,scala的写法要更加简洁,
// scala val s = if (x>0) 1 else -1 // java int s; if (x>0) { s = 1; } else { s = -1; }
scala中每个表达式实际上都有一个类型,就是它的返回类型。
如果是if/else这种可能使用mixed-type,返回的就是它们的supertype。
val s = if (x>0) "possible" else -1 // s is Any
如果只是if,
val s = if (x>0) "possible" // equals: if (x>0) "possible" else () // () is Unit, ()可以看作是占位符, Unit就是void
Statement Termination
scala中默认不写分号,除非一行有多条语句,很好的语法特性
Block Expressions and Assignments
跟Java一样,{ }包围着的就是一个Block,最后一句就是整个Block的值 (赋值语句的没有返回值,或者说返回的是Unit)
Input and Output
print:
print("Answer is ") println("Answer is ") printf("Anser is %s, with %d", "fred", 42)
read:
val name = readLine("Your name: ") val age = readInt() val salary = readDouble() // readInt, readDouble, readByte, readShort, readLong, readFloat, readBoolean, readChar
Loops
while, do-while都和java的用法一样
for的变化很大,实际上for并不常用
for (i <- expr) // 意思是i将遍历右侧表达式中的每一个值
Advanced for Loops and for Comprehensions
一些基本的写法
for (i <- 1 to 3; j <- 1 to 3) print((10*i + j) + " ") // 11, 12, 13, 21, 22, 23, 31, 32, 33 for (i <- 1 to 3; j <- 1 to 3 if i != j) print((10*i + j) + " ") // 12, 13, 21, 23, 31, 32 for (i <- 1 to 3; from = 4 - i; j <- from to 3) print((10*i + j) + " ") // 13, 22, 23, 31, 32, 33
stackoverflow上关于for comprehension的讨论
for (c <- "Hello"; i <- 0 to 1) yield (c + i).toChar // String: HIeflmlmop for (i <- 0 to 1; c <- "Hello") yield (c + i).toChar // Char array: ['H', 'I', 'e', 'f', 'l', 'm', 'l', 'm', 'o', 'p']
Functions
scala中的Function可以独立于class存在
def abs(x: Double) = if (x > 0) x else -x
除非是递归,否则不需要指定Function的返回值,编译器会自动推断返回值的类型
Default and Named Arguments
scala中,你可以为函数参数指定默认值
def decorate(str: String, left: String = "[", right: String = "]") = left + str + right
调用函数的时候,也可以不按参数顺序,只要提供名称就好
decorate(left = ">>>", str = "wrap me", right = "<<<")
// >>>wrap me<<<
Variable Arguments
// 此时的参数类型是Seq def sum(args: Int*) = { var result = 0 for (arg <- args) result += arg result }
如果你有了一个Sequence,你需要告诉编译器你希望传入的时候作为sequence来处理:
val s = sum(1 to 5: _*)
更加FP的sum实现:
def sum(args: Int*) : Int = { if (args.length == 0) return 0 else args.head += sum(args.tail: _*) } // args.head is initial element // args.tail is a sequence of all other elements
Procedues
没有返回值 (或者说返回值是Unit) 的Function,就叫做Procedue
// 定义的时候不需要 = def box(s: String) { println("|" + s + "|\n") } // 或者 def box(s: String) : Unit = { println("|" + s + "|\n") }
Lazy Values
只要变量声明时候加了lazy,你不用就不会被初始化
lazy val words = scala.io.Source.fromFile("/tmp/non-existed.log").mkString // 编译没问题。运行时,除非有代码调用到了words,否则不会去检查这个文件读取的时候有没有问题
val, lazy and def
val words = scala.io.Source.fromFile("/tmp/my.log").mkString // Evaluated as soon as words is defined lazy val words = scala.io.Source.fromFile("/tmp/my.log").mkString // Evaluated the first time words is used def words = scala.io.Source.fromFile("/tmp/my.log").mkString // Evaluated every time words is used
Exceptions
scala中没有checked exception, 意味着写代码的时候不需要声明一堆throw AAAException, BBBException.
在try-catch中,异常捕捉是基于pattern-matching的,跟java相比显得格外简洁
try { process(new URL("http://www.example.com/test.gif")) } catch { case _: MailFormedURLException => println("Bad URL: " + url) case ex: IOException => ex.printStackTrace() } // use _ for the variable name if you don't need it
在try-finally中,finally跟java一样,无论如何都会被执行的
in = new URL("http://www.example.com/test.gif").openStream() try { process(in) } finally { in.close() }