工作如何繁忙,生活如何糟心,至少求知的这一刻是我的…
前言
基于Scala 2.12.14。学的《Scala for the Impatient》,记录每天的笔记,有些名词看的英文,翻得未必准确。
适用对象:Java程序员。尽可能在短时间内建立起Java到Scala的转换,做到能看能写。实际上在学习一门语言,应当逐步抛弃掉固有成见,才能发现其中乐趣。人生苦短,别用Java:(逃。
今日重点
了解Scala中的程序控制流,包含有:
- 函数
- 过程
- Lazy Value
- 异常
函数
函数可以不依赖于对象,方法需要基于一个对象进行操作。Scala中是可以直接定义函数的,而不必像Java一样用静态方法来实现函数。
Scala中函数的定义大体如:
def foo(n: Int): Int = {if (0 < n) 1 else -1}
// 如果还不理解,拆开来看:
// def 定义函数/方法的关键字
// foo 函数名
// n: Int 定义了一个名为n,类型为Int的入参
// : Int = ... 定义了函数返回类型
// {if (0 < n) 1 else -1} 函数内操作,表达式等
Scala编译器支持返回类型自动推导的,只要不是递归函数,就可以省略定义函数返回类型:
def foo(n: Int) = {if (0 < n) 1 else -1}
试想一个斐波那契数的递归实现:
def fac(n: Int): Int = if (n <= 0) 1 else n * fac(n - 1)
// 分支 n <= 0 返回1,尚可推导类型为Int
// 分支 else 对于Scala编译器就无法确定它的类型为Int
默认参数与命名参数
类似Python,Scala允许为方法/函数入参提供默认值,以及通过指定参数命名的方式为特定参数赋值:
def log(info: String, level: String = "INFO", prefix: String = "[", suffix: String = "]") = {
prefix + " " + level + " " + suffix + info
}
log("sth", suffix="}")
// res4: String = [ INFO }sth
没有指定命名的参数必须在命名参数之前,不能出现:
}
log(suffix="}", "sth")
/*
<console>:13: error: not enough arguments for method log: (info: String, level: String, prefix: String, suffix: String)String.
Unspecified value parameter info.
log(suffix="a", "a", "debug")
*/
这样是有歧义的,"sth"究竟是哪个参数的值呢?因此异常在预料之中。
变长参数
Scala入参支持变长参数,可以通过循环遍历变长参数或用apply方式访问它的具体index下的值:
def pretty_print(args: Int*) = {
for (arg <- args) {
println(arg)
}
println(args(0))
}
pretty_print(3, 5, 7)
// 3 5 7 3
在之前我们学过,1 to 3会生成一个[1, 3]区间的序列,但它的类型是Range,要告诉编译器将其转换为Seq:
pretty_print(1 to 3: _*)
_*也就是告知编译器要将参数转换为Seq。
当调用了java方法且其参数为变长Object时,即Object…,需要将基本类型(Scala没有基本类型,只是类比Java的那些类型)转换为Object类型,即Scala中的AnyRef类型:
import java.text.MessageFormat
val str = MessageFormat.format("{0} {1}", "nothing matters", 1.asInstanceOf[AnyRef])
// 试试这个?
// MessageFormat.format("{0} {1}", "nothing matters", 1)
过程
当函数/方法没有返回值时,该函数可被称为过程。还记得上边的pretty_print吗,它就是过程。
没有返回值时,函数的返回值类型可以省略,也可以显式指定Unit:
def pretty_print(args: Int*): Unit = {
for (arg <- args) {
println(arg)
}
}
延迟加载(Lazy Values)
有些对象初始化的代价很大,且使用频度不高甚至未必在应用运行期间使用到,那么可以延迟其初始化到它使用时。
Scala有两种定义惰性变量的关键字,lazy和def:
def lazyDemo(): String = {
println("lazyDemo")
return "lazyDemo"
}
lazy val ld = lazyDemo()
// ld: String = <lazy>
ld
// lazyDemo
// res0: String = lazyDemo
ld
// res1: String = lazyDemo
def defDemo(): String = {
println("defDemo")
return "defDemo"
}
def dd = defDemo
// dd: String
dd
// defDemo
// res2: String = defDemo
dd
// defDemo
// res3: String = defDemo
def定义的变量在每次使用时都会加载,lazy定义的变量只有在第一次使用时会加载。
异常
抛出异常
和Java一样的抛出异常方式:
throw new RuntimeException()
捕获异常
和Java基本一致,越通用的异常越靠后:
try {
// do sth
} catch {
case e: IOException => e.printStackTrace()
// 不关心的异常可以用_
case _: RuntimeException => println("it doesn't matters")
}
finally
与Java一致,有try {…} finally {…},也有try {…} catch {…} finally {…}。
有try with resource吗?
很遗憾,没有,要在finally中回收资源。