ZetCode

Kotlin 属性关键字

最后修改于 2025 年 4 月 19 日

Kotlin 的属性系统提供了一种简洁的方式来管理类状态。property 概念用内置的访问器替换了字段。本教程通过实际例子深入探讨属性。

基本定义

在 Kotlin 中,属性是一个类成员,它将字段与访问器结合起来。属性会自动生成 getter 和 setter。它们可以使用 val(只读)或 var(可变)声明。属性提供了封装,无需样板代码。

基本属性声明

最简单的属性声明由类型和可选的初始化器组成。Kotlin 自动提供默认访问器。这是一个基本的使用属性的例子。

BasicProperty.kt
package com.zetcode

class Person {
    var name: String = "John Doe"
    val age: Int = 30
}

fun main() {

    val person = Person()
    println(person.name) // Output: John Doe
    person.name = "Jane Smith" // Setter called
    println(person.name) // Output: Jane Smith
}

此示例显示了一个可变的 name 属性和只读的 age 属性。name 可以更改,而 age 则不能。Kotlin 自动生成默认的 getter 和 setter。

自定义 Getter 和 Setter

属性可以在属性声明后定义自定义访问器。getset 块允许自定义逻辑。setter 参数通常命名为 value

CustomAccessors.kt
package com.zetcode

class Rectangle(val width: Int, val height: Int) {
    val area: Int
        get() = width * height
        
    var borderColor: String = "black"
        set(value) {
            if (value in listOf("black", "red", "blue")) {
                field = value
            }
        }
}

fun main() {

    val rect = Rectangle(10, 20)
    println(rect.area) // Output: 200
    
    rect.borderColor = "red"
    println(rect.borderColor) // Output: red
}

这里 area 是一个带有自定义 getter 的计算属性。borderColor 具有带有验证的 setter。field 标识符引用后备字段。无效的颜色被默默忽略。

后备字段

Kotlin 在需要时自动提供后备字段,通过 field 标识符访问。当自定义访问器引用它们时,后备字段存储属性值。它们仅在访问器中使用时才生成。

BackingField.kt
package com.zetcode

class Counter {
    var count: Int = 0
        set(value) {
            if (value >= 0) {
                field = value
            }
        }
        
    val isZero: Boolean
        get() = count == 0
}

fun main() {

    val counter = Counter()
    counter.count = 5
    println(counter.count) // Output: 5
    counter.count = -3 // Ignored
    println(counter.count) // Output: 5
    println(counter.isZero) // Output: false
}

count 属性使用后备字段来存储其值。setter 验证输入,忽略负数。isZero 是一个没有后备字段的计算属性,因为它不存储状态。

延迟初始化属性

lateinit 标记的属性可以在声明后初始化。它们必须是 var 且不可为空。当依赖注入或测试设置初始化属性时很有用。

LateInitProperty.kt
package com.zetcode

class Service {
    lateinit var apiClient: ApiClient
    
    fun initialize(client: ApiClient) {
        apiClient = client
    }
    
    fun callService() {
        if (::apiClient.isInitialized) {
            apiClient.call()
        }
    }
}

class ApiClient {
    fun call() = println("API called")
}

fun main() {

    val service = Service()
    service.initialize(ApiClient())
    service.callService() // Output: API called
}

apiClient 在没有初始化器的情况下声明,但必须在使用前设置。isInitialized 检查可防止 UninitializedPropertyAccessException。这种模式在 Spring 等框架中很常见。

委托属性

Kotlin 使用 by 关键字支持属性委托。委托处理属性访问逻辑。常见的委托包括 lazyobservablevetoable

DelegatedProperty.kt
package com.zetcode

import kotlin.properties.Delegates

class User {
    val lazyValue: String by lazy {
        println("computed!")
        "Hello"
    }
    
    var name: String by Delegates.observable("<no name>") {
        prop, old, new ->
        println("$old -> $new")
    }
}

fun main() {

    val user = User()
    println(user.lazyValue) // Output: computed! then Hello
    println(user.lazyValue) // Output: Hello (cached)
    
    user.name = "John" // Output: <no name> -> John
    user.name = "Jane" // Output: John -> Jane
}

lazyValue 仅在第一次访问时计算并缓存。name 通过 observable 委托更改时通知。委托封装了常见的属性模式,减少了样板代码。

属性可见性

属性可见性修饰符控制对属性及其访问器的访问。Getter 继承属性可见性,而 setter 可以具有单独的修饰符。这使得封装更加灵活。

PropertyVisibility.kt
package com.zetcode

class BankAccount {
    var balance: Double = 0.0
        private set
    
    fun deposit(amount: Double) {
        if (amount > 0) {
            balance += amount
        }
    }
    
    fun withdraw(amount: Double): Boolean {
        if (amount <= balance) {
            balance -= amount
            return true
        }
        return false
    }
}

fun main() {

    val account = BankAccount()
    account.deposit(100.0)
    println(account.balance) // Output: 100.0
    // account.balance = 200.0 // Compile error
}

balance 属性具有公共 getter 但私有 setter。更改必须通过受控方法 depositwithdraw 进行。这确保了适当的验证和业务规则。

扩展属性

Kotlin 允许通过扩展向现有类添加属性。扩展属性没有后备字段。它们必须定义显式 getter(和 var 的 setter)。

ExtensionProperty.kt
package com.zetcode

val String.hasDigits: Boolean
    get() = this.any { it.isDigit() }

var StringBuilder.lastChar: Char
    get() = this[this.length - 1]
    set(value) {
        this.setCharAt(this.length - 1, value)
    }

fun main() {

    val text = "Hello123"
    println(text.hasDigits) // Output: true
    
    val builder = StringBuilder("Kotlin")
    builder.lastChar = '!'
    println(builder) // Output: Kotli!
}

hasDigits 是一个扩展属性,用于检查字符串中的数字。lastChar 用可变的最后一个字符访问权限扩展 StringBuilder。扩展属性增强了现有类型,无需继承。

属性的最佳实践

来源

Kotlin 属性文档

本教程深入介绍了 Kotlin 属性,展示了声明、访问器、委托和扩展。属性提供了一种通过简洁的语法管理类状态的强大方法。正确使用属性可以编写更清晰、更易于维护的 Kotlin 代码。

作者

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

列出 所有 Kotlin 教程