ZetCode

Kotlin 值关键字

最后修改于 2025 年 4 月 19 日

Kotlin 的 `value` 关键字(与 `val` 一起使用)声明不可变变量,这些变量不能被重新赋值。 不可变变量是 Kotlin 采用更安全、更可预测的代码的关键特性。 本教程深入探讨了 `val` 关键字,并提供了实用示例。

基本定义

Kotlin 中的 `val` 关键字声明只读(不可变)变量。 一旦赋值,`val` 就不能被重新赋值。 这与声明可变变量的 `var` 不同。 不可变变量有助于防止意外修改。

基本 val 声明

`val` 最简单的用法是声明一个具有固定值的不可变变量。 该值可以被赋值一次,之后不能被更改。

BasicVal.kt
package com.zetcode

fun main() {

    val message = "Hello, Kotlin"
    // message = "Changed" // Compilation error
    
    println(message) // Output: Hello, Kotlin
}

在这里,我们声明一个名为 `message` 的 `val` 并为其分配一个字符串值。 尝试重新分配它将导致编译错误。 该值在其作用域内保持不变。

带有显式类型的 val

您可以在声明 `val` 时显式指定其类型。 当您想要清楚地说明变量的类型或无法推断类型时,这很有用。

TypedVal.kt
package com.zetcode

fun main() {

    val count: Int = 42
    val pi: Double = 3.14159
    
    println(count) // Output: 42
    println(pi) // Output: 3.14159
}

`count` 和 `pi` 都使用显式类型声明。 初始化后,这些值不能被更改。 显式类型有助于使代码更具可读性和可维护性。

带有计算值的 val

`val` 可以使用计算值进行初始化。 计算只发生一次,在初始化时,结果变为不可变。

ComputedVal.kt
package com.zetcode

fun main() {

    val radius = 5.0
    val area = Math.PI * radius * radius
    
    println("Area: $area") // Output: Area: 78.53981633974483
}

`area` val 由 `radius` 和 `Math.PI` 计算得到。 计算后,面积不能被更改。 这种模式对于不应在计算后更改的派生值很常见。

类属性中的 val

类属性可以声明为 `val` 以使其不可变。 这些属性必须在声明或构造函数中初始化。

ClassVal.kt
package com.zetcode

class Person(val name: String, val age: Int)

fun main() {

    val person = Person("Alice", 30)
    // person.name = "Bob" // Compilation error
    
    println("${person.name} is ${person.age}") // Output: Alice is 30
}

`Person` 类有两个不可变属性。 创建 `Person` 后,其姓名和年龄不能更改。 这确保了对象的状态保持一致。

带有自定义 getter 的 val

`val` 可以有一个自定义 getter,它会在每次访问时计算其值。 尽管是一个 `val`,但返回的值可能会根据其他状态而变化。

CustomGetter.kt
package com.zetcode

class Circle(val radius: Double) {
    val area: Double
        get() = Math.PI * radius * radius
}

fun main() {

    val circle = Circle(5.0)
    println(circle.area) // Output: 78.53981633974483
    
    // circle.area = 100.0 // Compilation error
}

`area` 属性会在每次访问时计算,但不能直接设置。 如果 `radius` 发生变化,该值会发生变化,但该属性仍为只读。

函数参数中的 val

Kotlin 中的函数参数始终是不可变的,类似于使用 `val` 声明。 您不能在函数体中重新分配它们。

FunctionVal.kt
package com.zetcode

fun greet(name: String) {
    // name = "Bob" // Compilation error
    println("Hello, $name!")
}

fun main() {

    greet("Alice") // Output: Hello, Alice!
}

`greet` 函数中的 `name` 参数是不可变的。 这可以防止参数的意外重新分配,这可能会导致复杂函数中的错误。

带有惰性初始化的 val

可以使用 `by lazy` 委托与 `val` 一起实现惰性初始化。 该值仅在首次访问时计算,然后被缓存。

LazyVal.kt
package com.zetcode

fun main() {

    val lazyValue: String by lazy {
        println("Computed!")
        "Hello"
    }
    
    println(lazyValue) // Output: Computed! then Hello
    println(lazyValue) // Output: Hello (no recomputation)
}

`lazyValue` 仅在首次访问时计算。 后续访问将返回缓存值。 这对于可能不需要的昂贵操作很有用。

val 的最佳实践

来源

Kotlin 属性文档

本教程深入探讨了 Kotlin 的 `val` 关键字,展示了声明和使用不可变变量的各种方法。 我们探讨了基本声明、类属性、自定义 getter 和惰性初始化。 正确使用不可变性可以使您的代码更安全,更容易推理。

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有多年的编程经验。 自 2007 年以来,我一直在撰写编程文章。 到目前为止,我撰写了 1400 多篇文章和 8 本电子书。 我拥有超过八年的编程教学经验。

列出 所有 Kotlin 教程