Scala 变量
变量是程序中存储数据的基本单元。本章将详细介绍 Scala 中变量的声明、初始化和使用方法。
变量声明
Scala 提供两种变量声明方式:val(不可变)和 var(可变)。
val - 不可变变量
scala
// 基本声明
val name = "Alice" // 类型推断为 String
val age = 25 // 类型推断为 Int
val height = 5.6 // 类型推断为 Double
// 显式类型声明
val count: Int = 10
val message: String = "Hello, Scala!"
val isActive: Boolean = true
// 一旦赋值,不能再次修改
// name = "Bob" // 编译错误!var - 可变变量
scala
// 基本声明
var counter = 0 // 类型推断为 Int
var status = "pending" // 类型推断为 String
// 显式类型声明
var score: Double = 0.0
var items: List[String] = List.empty
// 可以重新赋值
counter = counter + 1 // OK
status = "completed" // OK
score = 95.5 // OK选择 val 还是 var
scala
// 推荐:优先使用 val
val configuration = Map(
"host" -> "localhost",
"port" -> 8080,
"timeout" -> 30
)
// 只在必要时使用 var
var attempts = 0
while (attempts < 3) {
// 尝试连接
attempts += 1
}
// 函数式风格替代 var
val results = (1 to 3).map { attempt =>
// 尝试连接并返回结果
s"Attempt $attempt"
}变量初始化
立即初始化
scala
val immediate = 42 // 立即初始化
var mutable = "initial" // 立即初始化延迟初始化
scala
// lazy val - 延迟初始化,只在首次访问时计算
lazy val expensiveComputation = {
println("Computing...")
Thread.sleep(1000) // 模拟耗时操作
42 * 42
}
println("Before access")
println(expensiveComputation) // 此时才执行计算
println(expensiveComputation) // 直接返回缓存的结果条件初始化
scala
val environment = sys.env.getOrElse("ENV", "development")
val config = if (environment == "production") {
Map("db" -> "prod-db", "cache" -> "redis")
} else {
Map("db" -> "test-db", "cache" -> "memory")
}
// 使用 Option 处理可能不存在的值
val maybePort: Option[Int] = sys.env.get("PORT").map(_.toInt)
val port = maybePort.getOrElse(8080)作用域和生命周期
局部变量
scala
def example(): Unit = {
val localVar = "local" // 方法作用域
if (true) {
val blockVar = "block" // 块作用域
println(localVar) // 可以访问外层变量
println(blockVar) // 可以访问当前块变量
}
// println(blockVar) // 编译错误:超出作用域
}变量遮蔽
scala
val x = 10 // 外层变量
def shadowExample(): Unit = {
val x = 20 // 遮蔽外层变量
println(x) // 输出 20
{
val x = 30 // 进一步遮蔽
println(x) // 输出 30
}
println(x) // 输出 20
}类成员变量
scala
class Person(val name: String, var age: Int) {
// 主构造函数参数自动成为成员变量
private val id = java.util.UUID.randomUUID() // 私有成员
protected var status = "active" // 受保护成员
def birthday(): Unit = {
age += 1 // 修改可变成员变量
}
def getId: String = id.toString // 访问私有成员
}
val person = new Person("Alice", 25)
println(person.name) // 访问 val 成员
person.age = 26 // 修改 var 成员
person.birthday() // 通过方法修改类型推断
基本类型推断
scala
val int = 42 // Int
val long = 42L // Long
val double = 3.14 // Double
val float = 3.14f // Float
val string = "hello" // String
val boolean = true // Boolean
val char = 'A' // Char
// 集合类型推断
val list = List(1, 2, 3) // List[Int]
val map = Map("a" -> 1, "b" -> 2) // Map[String, Int]
val set = Set(1, 2, 3, 2) // Set[Int]复杂类型推断
scala
// 函数类型推断
val add = (x: Int, y: Int) => x + y // (Int, Int) => Int
val square = (x: Int) => x * x // Int => Int
// 高阶函数类型推断
val numbers = List(1, 2, 3, 4, 5)
val doubled = numbers.map(_ * 2) // List[Int]
val filtered = numbers.filter(_ > 2) // List[Int]
// Option 类型推断
val some = Some(42) // Some[Int]
val none = None // None.type
val option: Option[Int] = if (true) Some(42) else None需要显式类型的情况
scala
// 空集合需要类型注解
val emptyList: List[String] = List()
val emptyMap: Map[String, Int] = Map()
// 递归函数需要返回类型
def factorial(n: Int): Int = {
if (n <= 1) 1 else n * factorial(n - 1)
}
// 重载方法调用时可能需要类型注解
def process(x: Int): String = s"Int: $x"
def process(x: String): String = s"String: $x"
val result: String = process(42) // 明确调用 Int 版本变量模式匹配
解构赋值
scala
// 元组解构
val tuple = ("Alice", 25, "Engineer")
val (name, age, profession) = tuple
// 列表解构
val list = List(1, 2, 3, 4, 5)
val head :: tail = list // head = 1, tail = List(2, 3, 4, 5)
val first :: second :: rest = list // first = 1, second = 2, rest = List(3, 4, 5)
// 数组解构
val Array(a, b, c) = Array(1, 2, 3)
// 案例类解构
case class Point(x: Int, y: Int)
val point = Point(3, 4)
val Point(x, y) = point // x = 3, y = 4部分解构
scala
val numbers = List(1, 2, 3, 4, 5)
// 只取前两个元素
val first :: second :: _ = numbers
// 使用通配符忽略不需要的部分
val (name, _, profession) = ("Alice", 25, "Engineer")
// Option 解构
val maybeValue: Option[String] = Some("hello")
val Some(value) = maybeValue // 危险:如果是 None 会抛异常
// 安全的 Option 处理
maybeValue match {
case Some(v) => println(s"Value: $v")
case None => println("No value")
}变量的不变性
深度不变性 vs 浅度不变性
scala
// val 只保证引用不变,不保证内容不变
val mutableList = scala.collection.mutable.ListBuffer(1, 2, 3)
// mutableList = scala.collection.mutable.ListBuffer(4, 5, 6) // 错误
mutableList += 4 // OK:修改内容
// 真正的不变性
val immutableList = List(1, 2, 3)
// immutableList = List(4, 5, 6) // 错误
val newList = immutableList :+ 4 // OK:创建新列表
// 不可变的案例类
case class Person(name: String, age: Int)
val person = Person("Alice", 25)
// person.age = 26 // 错误:案例类字段默认是 val
val olderPerson = person.copy(age = 26) // OK:创建新实例函数式更新
scala
// 使用 copy 方法更新案例类
case class User(name: String, email: String, age: Int)
val user = User("Alice", "alice@example.com", 25)
val updatedUser = user.copy(age = 26)
val renamedUser = user.copy(name = "Alicia")
// 链式更新
val fullyUpdated = user
.copy(name = "Alicia")
.copy(age = 26)
.copy(email = "alicia@example.com")变量的最佳实践
命名约定
scala
// 好的命名
val userName = "alice" // 驼峰命名
val maxRetryCount = 3 // 清晰的含义
val isAuthenticated = true // 布尔值用 is/has 前缀
// 常量使用大写
val MAX_CONNECTIONS = 100
val DEFAULT_TIMEOUT = 30
// 避免的命名
val x = "alice" // 不清晰
val data = List(1, 2, 3) // 太泛化
val flag = true // 不明确作用域最小化
scala
// 好的做法:最小作用域
def processData(input: List[Int]): List[Int] = {
val threshold = 10 // 只在需要的地方声明
input.filter { value =>
val adjusted = value * 2 // 在最小作用域内声明
adjusted > threshold
}
}
// 避免:过大的作用域
def processDataBad(input: List[Int]): List[Int] = {
val threshold = 10
val adjusted = 0 // 不必要的提前声明
// ... 很多代码
input.filter(_ * 2 > threshold)
}使用 Option 代替 null
scala
// 好的做法
def findUser(id: Int): Option[User] = {
if (id > 0) Some(User(s"User$id", s"user$id@example.com", 25))
else None
}
val user = findUser(1)
val userName = user.map(_.name).getOrElse("Unknown")
// 避免使用 null
def findUserBad(id: Int): User = {
if (id > 0) User(s"User$id", s"user$id@example.com", 25)
else null // 不好的做法
}实际应用示例
配置管理
scala
object Configuration {
// 不可变配置
val appName: String = "MyScalaApp"
val version: String = "1.0.0"
// 从环境变量读取配置
lazy val port: Int = sys.env.get("PORT").map(_.toInt).getOrElse(8080)
lazy val host: String = sys.env.getOrElse("HOST", "localhost")
lazy val dbUrl: String = sys.env.getOrElse("DB_URL", "jdbc:h2:mem:test")
// 复合配置
lazy val serverConfig = ServerConfig(host, port)
lazy val dbConfig = DatabaseConfig(dbUrl, "user", "password")
}
case class ServerConfig(host: String, port: Int)
case class DatabaseConfig(url: String, username: String, password: String)状态管理
scala
class Counter {
private var _count: Int = 0
def count: Int = _count // 只读访问
def increment(): Unit = {
_count += 1
}
def decrement(): Unit = {
_count -= 1
}
def reset(): Unit = {
_count = 0
}
}
// 不可变版本
case class ImmutableCounter(count: Int = 0) {
def increment: ImmutableCounter = copy(count = count + 1)
def decrement: ImmutableCounter = copy(count = count - 1)
def reset: ImmutableCounter = copy(count = 0)
}练习
练习 1:变量声明和使用
scala
object VariableExercise {
def main(args: Array[String]): Unit = {
// 1. 声明不可变变量
val firstName = "John"
val lastName = "Doe"
val age = 30
// 2. 声明可变变量
var score = 0
var level = 1
// 3. 使用字符串插值
val fullName = s"$firstName $lastName"
println(s"Player: $fullName, Age: $age")
// 4. 修改可变变量
score += 100
level += 1
println(s"Score: $score, Level: $level")
// 5. 使用 lazy val
lazy val expensiveCalculation = {
println("Performing expensive calculation...")
(1 to 1000000).sum
}
println("Before accessing lazy val")
println(s"Result: $expensiveCalculation")
println(s"Result again: $expensiveCalculation")
}
}练习 2:模式匹配和解构
scala
object PatternMatchingExercise {
case class Student(name: String, grades: List[Int], major: String)
def analyzeStudent(student: Student): Unit = {
// 解构案例类
val Student(name, grades, major) = student
println(s"Analyzing student: $name")
println(s"Major: $major")
// 解构列表
grades match {
case Nil => println("No grades available")
case head :: Nil => println(s"Only one grade: $head")
case first :: second :: rest =>
println(s"First grade: $first, Second grade: $second")
println(s"Remaining grades: ${rest.mkString(", ")}")
}
// 计算平均分
val average = if (grades.nonEmpty) grades.sum.toDouble / grades.length else 0.0
println(f"Average grade: $average%.2f")
}
def main(args: Array[String]): Unit = {
val students = List(
Student("Alice", List(85, 92, 78, 96), "Computer Science"),
Student("Bob", List(88, 91), "Mathematics"),
Student("Charlie", List(), "Physics")
)
students.foreach(analyzeStudent)
}
}总结
本章详细介绍了 Scala 变量的核心概念:
- 变量类型:
val(不可变)vsvar(可变) - 初始化方式:立即初始化、延迟初始化、条件初始化
- 作用域规则:局部变量、成员变量、变量遮蔽
- 类型推断:自动类型推断和显式类型声明
- 模式匹配:解构赋值和模式匹配
- 最佳实践:不变性、命名约定、作用域控制
理解变量的正确使用是编写高质量 Scala 代码的基础。在下一章中,我们将学习 Scala 访问修饰符,了解如何控制代码的访问权限。