变量与常量 #

一、val 与 var #

1.1 val - 不可变变量 #

val 声明的变量是不可变的,一旦赋值就不能改变:

scala
val name = "Scala"
name = "Java"

类似于 Java 的 final 变量。

1.2 var - 可变变量 #

var 声明的变量是可变的:

scala
var counter = 0
counter = 1
counter = 2
println(counter)

1.3 选择原则 #

优先使用 val,原因:

  1. 代码更安全,减少意外修改
  2. 并发编程更安全
  3. 代码更易理解和推理
  4. 函数式编程风格
scala
val pi = 3.14159
val greeting = "Hello, World!"
val numbers = List(1, 2, 3)

二、类型推断 #

2.1 自动类型推断 #

Scala 会自动推断变量类型:

scala
val name = "Scala"
val age = 25
val pi = 3.14159
val flag = true

println(s"name: ${name.getClass}")  // class java.lang.String
println(s"age: ${age.getClass}")    // int
println(s"pi: ${pi.getClass}")      // double
println(s"flag: ${flag.getClass}")  // boolean

2.2 显式类型注解 #

有时需要显式指定类型:

scala
val name: String = "Scala"
val age: Int = 25
val pi: Double = 3.14159
val flag: Boolean = true

2.3 需要显式类型的场景 #

指定特定类型:

scala
val number: Long = 100
val decimal: Float = 3.14f
val chars: Seq[Char] = "Hello".toSeq

避免类型推断歧义:

scala
val empty: List[String] = List()
val nothing: Option[Int] = None

公共 API:

scala
def greet(name: String): String = s"Hello, $name!"

三、延迟初始化 #

3.1 lazy val #

lazy val 延迟初始化,直到第一次访问时才计算:

scala
lazy val expensiveValue = {
  println("Computing...")
  Thread.sleep(1000)
  42
}

println("Before access")
println(expensiveValue)
println(expensiveValue)

输出:

text
Before access
Computing...
42
42

3.2 lazy val 的用途 #

  • 延迟昂贵的初始化
  • 处理循环依赖
  • 按需加载资源
scala
lazy val database = connectToDatabase()
lazy val config = loadConfig()

def processData() =
  val db = database
  val cfg = config

四、变量的作用域 #

4.1 局部变量 #

在方法或代码块内定义:

scala
def calculate(): Int =
  val x = 10
  val y = 20
  x + y

4.2 成员变量 #

在类或对象中定义:

scala
class Counter:
  var count = 0
  
  def increment(): Unit =
    count += 1
  
  def current: Int = count

4.3 遮蔽(Shadowing) #

内部作用域可以遮蔽外部变量:

scala
val x = 10

def demo() =
  val x = 20
  println(s"Inner x: $x")

println(s"Outer x: $x")
demo()

输出:

text
Outer x: 10
Inner x: 20

五、不可变性与可变性 #

5.1 val 引用不可变 #

val 保证引用不可变,但引用的对象可能是可变的:

scala
val buffer = scala.collection.mutable.ListBuffer(1, 2, 3)
buffer += 4
println(buffer)

5.2 不可变集合 #

使用不可变集合保证完全不可变:

scala
val list = List(1, 2, 3)
val newList = list :+ 4

println(list)
println(newList)

5.3 不可变性的好处 #

scala
val config = Map(
  "host" -> "localhost",
  "port" -> "8080"
)

def getConfig = config

六、类型转换 #

6.1 数值类型转换 #

scala
val intVal = 100
val longVal: Long = intVal
val doubleVal: Double = intVal

6.2 强制类型转换 #

scala
val doubleVal = 3.14
val intVal = doubleVal.toInt

val longVal = 100L
val intVal2 = longVal.toInt

6.3 字符串转换 #

scala
val numStr = "123"
val num = numStr.toInt

val doubleStr = "3.14"
val doubleVal = doubleStr.toDouble

val boolStr = "true"
val boolVal = boolStr.toBoolean

七、占位符初始化 #

7.1 使用 _ 初始化 #

var 变量可以使用 _ 初始化为默认值:

scala
var x: Int = _
var y: Double = _
var z: Boolean = _
var s: String = _

println(x)
println(y)
println(z)
println(s)

7.2 各类型默认值 #

类型 默认值
Int 0
Long 0L
Float 0.0f
Double 0.0
Boolean false
Char ‘\0’
引用类型 null

7.3 注意事项 #

val 不能使用 _ 初始化:

scala
val x: Int = _

八、多变量声明 #

8.1 元组解构 #

scala
val (name, age) = ("Alice", 25)
println(s"$name is $age years old")

8.2 模式匹配解构 #

scala
val person @ (firstName, lastName) = ("John", "Doe")
println(s"Full name: ${person._1} ${person._2}")

8.3 集合解构 #

scala
val List(a, b, c) = List(1, 2, 3)
println(s"a=$a, b=$b, c=$c")

val head :: tail = List(1, 2, 3, 4, 5)
println(s"head=$head, tail=$tail")

九、最佳实践 #

9.1 优先使用 val #

scala
val result = calculate()
val message = if (result > 0) "positive" else "non-positive"

9.2 使用有意义的名称 #

scala
val maxRetryCount = 3
val connectionTimeout = 5000

var currentIndex = 0
var retryCount = 0

9.3 最小化可变状态 #

scala
def factorial(n: Int): Int =
  @annotation.tailrec
  def loop(acc: Int, n: Int): Int =
    if n <= 1 then acc
    else loop(acc * n, n - 1)
  loop(1, n)

9.4 使用不可变数据结构 #

scala
val items = List(1, 2, 3)
val newItems = items :+ 4
val filtered = items.filter(_ > 1)

十、总结 #

特性 val var lazy val
可变性 不可变 可变 不可变
初始化时机 立即 立即 延迟
线程安全
推荐程度 推荐 谨慎使用 按需使用

关键要点:

  • 优先使用 val,减少可变状态
  • 使用 lazy val 延迟昂贵的初始化
  • 理解 val 引用不可变与对象可变的区别
  • 使用有意义的变量名

下一步,让我们学习 基本数据类型

最后更新:2026-03-27