Scala 3 起步:表达式、函数与不可变集合
工具链、编译与首次运行
Scala同时支持面向对象与函数式组织,本课程选JVM平台,不要求先学Java或Spark。先解释三个工具:JDK提供java/javac与诊断工具;scalac把Scala编译为类文件;scala启动器可能基于Scala CLI并负责构建运行。不要把启动器版本与语言版本混为一谈。本批核对2026-10-17,使用官方Scala3.10.0发行、Temurin JDK21.0.12.1+1。旧Scala2文章语法不能无条件当Scala3语法。
Windows从官方下载选择安装方式;需要离线标准库示例时可解压官方Scala3发行ZIP。PowerShell在当前会话设置$env:JAVA_HOME='D:/tools/jdk-21',运行& "$env:JAVA_HOME/bin/java.exe" -version和完整路径scalac.bat。Linux解压JDK和Scala包,Bash会话设置export JAVA_HOME=/your/jdk和工具bin路径。下文以scalac/java为命令,不要求全局改系统配置。
Hello.scala完整内容:
object Hello:
def main(args: Array[String]): Unit =
println("Hello, Scala!")scalac -d out Hello.scala先创建out目录(PowerShell New-Item -ItemType Directory -Force out,Bash mkdir -p out)。启动时要同时包含out与Scala运行库;Windows java -cp "out;D:/tools/scala/lib/scala.jar" Hello,Linux java -cp 'out:/your/scala/lib/scala.jar' Hello。该lib布局对应本批官方发行包,若使用其他包则按其类路径实际布局;也可以在联网环境用scala run Hello.scala由CLI管理。预期Hello, Scala!。
val、类型、表达式与控制流
val绑定一次,var允许重赋;val指向可变对象时对象仍能改。Int是有界整数,Long更大但仍会溢出;Double是浮点,String是文本,Boolean条件不能用整数代替。Scala通过推断省去很多类型注解,但公开方法最好写清返回类型;Any不是动态类型魔法,用Any接收所有东西会丢失有用约束。
下面也是完整程序,缩进组成代码块,不要混乱对齐:
object Values:
def main(args: Array[String]): Unit =
val scores = List(80, 90, 100)
val total = scores.sum
val status = if total >= 270 then "passed" else "retry"
println(s"$status: $total")
for score <- scores do println(score)预期passed: 270后跟三项分数。if是表达式,返回分支值;do的循环主要执行副作用,yield构造结果集合。Unit表示无有用结果,类似只有一个值的类型;Nothing是没有正常返回值的底类型,如throw路径,不等于null。字符串插值前缀s,复杂表达式放${...}。JVM的String length按UTF-16代码单元。
方法、函数值与组合
def total(xs: List[Int]): Int = xs.sum是方法;val accepted: Int => Boolean = n => n >= 90是函数值。方法在需要函数的位置可转换成函数。scores.filter(accepted).map(_ * 2)用回调表达规则,先筛再变换;不要把复杂控制流压成看不懂的一行。参数列表可分组,using参数由上下文提供,详见类型建模。
函数式设计把“输入到结果”的计算与文件/日志副作用分开,让测试可直接给值检查结果。纯函数不是“没有for循环”,而是不依赖隐含可变状态、不产生外部可观察副作用。为了局部构建报告而使用只在函数内部可见的mutable Map可以有清晰边界,不必为形式纯洁复制全部状态。
List、Vector、Map、Iterator与复杂度
默认List/Vector/Map通常引用不可变集合。List适合头部构造与遍历,按索引访问是线性;循环里反复list(i)可能造成平方成本。Vector适合通用索引序列;Map表达键值但普通哈希Map不承诺业务排序,输出应显式sortBy。不可变集合更新返回新集合,旧值仍可使用;结构共享减少复制,不代表完全没有分配。
Iterator是一次性游标,消费后位置向前,不能像List一样反复sum;view提供惰性转换视图,底层若可变会影响后续结果。LazyList能惰性求值并缓存,但保留头部引用可能把已计算链也保留;需要处理大文件时Iterator加明确资源作用域通常更容易管理。不要返回引用已关闭Reader的Iterator。
练习:List的尾部追加为何不适合逐项构造大型结果?将if结果绑定val后是否还需临时var?完整解析/集合应用见分类账。参考解法:单链表头部操作便宜,反复尾加需遍历;选择构建器/Vector或前插后反转。if本来就返回值,不需要额外可变变量。
依据:Scala3 Book、集合、中文Scala Book。中文书包含Scala2时代写法,只比较讲解顺序,语法与API以Scala3资料与实际编译为准。
继续阅读
- 开发实践
- 类型、表达式与建模
- 类型、表达式与建模