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 教程。