Kotlin val 关键字
最后修改于 2025 年 4 月 19 日
Kotlin 的 val 关键字用于声明不可变变量。这些变量只能被赋值一次,并且不能被重新赋值。本教程将通过实际例子深入探讨 val 关键字。
基本定义
Kotlin 中的 val 关键字声明只读变量。一旦初始化,val 就不能被重新赋值。它类似于 Java 中的 final 变量。val 在引用级别上提供不可变性。
基本的 val 声明
val 的最简单用法是声明一个带有初始值的不可变变量。类型可以被显式声明或由 Kotlin 推断。
package com.zetcode
fun main() {
val name = "John Doe" // Type inferred as String
val age: Int = 30 // Explicit type declaration
println(name) // Output: John Doe
println(age) // Output: 30
}
这里我们声明了两个 val 变量。第一个具有推断类型,而第二个具有显式类型。这两个变量在初始化后都不能被重新赋值。这确保了它们的值保持不变。
val vs var
Kotlin 同时具有 val(不可变)和 var(可变)关键字。val 通过防止意外重新赋值来提供安全性。
package com.zetcode
fun main() {
val immutable = "I cannot change"
var mutable = "I can change"
// immutable = "New value" // Compilation error
mutable = "New value" // Allowed
println(immutable) // Output: I cannot change
println(mutable) // Output: New value
}
此示例显示了 val 和 var 之间的关键区别。尝试重新赋值 val 会导致编译错误。当值不应该改变时,始终优先使用 val。
val 与自定义类型
val 可用于任何 Kotlin 类型,包括自定义类。引用是不可变的,但对象的属性可能仍然是可变的。
package com.zetcode
class Person(var name: String, var age: Int)
fun main() {
val person = Person("Alice", 25)
// person = Person("Bob", 30) // Error: val cannot be reassigned
person.name = "Alice Smith" // Allowed: object is mutable
person.age = 26
println("${person.name}, ${person.age}") // Output: Alice Smith, 26
}
这里我们声明了一个 val,它包含一个 Person 对象。虽然我们不能重新赋值 person 变量,但我们可以修改其属性,因为 Person 类使用了 var 属性。这表明 val 仅保护引用。
val 在函数参数中
Kotlin 中的函数参数始终是不可变的,类似于使用 val 声明。你不能在函数体内重新赋值它们。
package com.zetcode
fun printMessage(message: String) {
// message = "New message" // Error: parameters are vals
println(message)
}
fun main() {
val msg = "Hello, Kotlin!"
printMessage(msg) // Output: Hello, Kotlin!
}
message 参数在函数内部的行为类似于 val。尝试重新赋值它会导致编译错误。这强制了函数参数的不可变性。
val 与延迟初始化
val 可以使用 lateinit(对于 var)或使用自定义 getter 进行延迟初始化。这在延迟初始化时很有用。
package com.zetcode
class Configuration {
val apiKey: String by lazy {
// Complex initialization
System.getenv("API_KEY") ?: "default-key"
}
}
fun main() {
val config = Configuration()
println(config.apiKey) // Initialized on first access
}
在这里,apiKey 是一个 val,它是延迟初始化的。该值仅在首次访问时计算。这种模式对于昂贵的初始化很有用。该值在初始化后保持不变。
val 在伴生对象中
伴生对象中的 val 创建类级别的常量。这些在加载类时被初始化,并在整个执行过程中保持不变。
package com.zetcode
class MathConstants {
companion object {
val PI = 3.14159
val E = 2.71828
}
}
fun main() {
println(MathConstants.PI) // Output: 3.14159
println(MathConstants.E) // Output: 2.71828
}
PI 和 E 值是类级别的常量。它们通过类名访问,并且不能被修改。这是数学常量和配置值的常见模式。
val 与集合
当将 val 与集合一起使用时,引用是不可变的,但集合内容可能仍然是可变的。使用不可变集合以实现完全的不可变性。
package com.zetcode
fun main() {
val mutableList = mutableListOf(1, 2, 3)
val immutableList = listOf(1, 2, 3)
mutableList.add(4) // Allowed
// immutableList.add(4) // Error: immutable collection
println(mutableList) // Output: [1, 2, 3, 4]
println(immutableList) // Output: [1, 2, 3]
}
这表明 val 仅使引用不可变。集合的可变性取决于其类型。为了实现完全的不可变性,请使用 listOf、setOf 或 mapOf。
val 使用的最佳实践
- 优先使用 val 而不是 var: 默认情况下使用
val,并且仅在需要修改时才使用var。 - 用于常量:
val非常适合在执行期间不应更改的值。 - 与不可变集合结合使用: 将
val与不可变集合配对以实现完全的不可变性。 - 考虑可见性: 对实现细节使用
private val,对公开的常量使用public val。 - 记录复杂的 vals: 当
val初始化不平凡或具有副作用时,添加注释。
来源
本教程深入介绍了 Kotlin 的 val 关键字,展示了它在不可变变量中的用法。我们探讨了各种场景,包括自定义类型、函数参数和集合。正确使用 val 可以使您的代码更安全、更可预测。
作者
列出 所有 Kotlin 教程。