Kotlin - 成员变量
在 Kotlin 中,成员变量(属性)是类的核心组成部分。Kotlin 提供了 var、val、lateinit、by lazy 等关键字,以及自定义 getter 和 setter,帮助开发者更灵活地管理对象的状态。这篇文章将深入介绍这些概念,并给出适用场景和最佳实践。
1. var 和 val
var(可变变量)
使用 var 关键字声明的变量是可变的,可以在后续代码中修改其值。
class Person {
var name: String = "Unknown"
}
fun main() {
val person = Person()
person.name = "Alice" // ✅ 允许修改
println(person.name) // 输出:Alice
}
val(不可变变量)
使用 val 关键字声明的变量是只读的,它的值在初始化后就不能被修改。
class Car {
val brand: String = "Toyota"
}
fun main() {
val car = Car()
// car.brand = "Honda" // ❌ 编译错误:val 不能被重新赋值
println(car.brand) // 输出:Toyota
}
📌 注意:val 变量的引用不可变,但如果它指向的是一个可变对象(如 List),对象本身的内容是可以修改的。
class DataHolder {
val numbers = mutableListOf(1, 2, 3)
}
fun main() {
val holder = DataHolder()
holder.numbers.add(4) // ✅ 允许修改列表内容
println(holder.numbers) // 输出:[1, 2, 3, 4]
}
2. lateinit(延迟初始化)
lateinit var
lateinit 关键字用于修饰 var 变量,表示该变量会稍后初始化,但不能用于 val。
class User {
lateinit var username: String
fun initialize() {
username = "JohnDoe"
}
}
fun main() {
val user = User()
user.initialize()
println(user.username) // 输出:JohnDoe
}
📌 注意:
lateinit 变量不能用于基本数据类型(如 Int、Double)。
如果在初始化前访问 lateinit 变量,会抛出 UninitializedPropertyAccessException。
fun main() {
val user = User()
println(user.username) // ❌ 抛出异常:UninitializedPropertyAccessException
}
3. by lazy(懒加载)
by lazy 适用于 只读(val)变量,它会在第一次访问时初始化,并且结果会被缓存。
class Config {
val settings: String by lazy {
println("Loading settings...")
"Dark Mode Enabled"
}
}
fun main() {
val config = Config()
println("Before accessing settings")
println(config.settings) // 第一次访问时会执行初始化
println(config.settings) // 之后访问直接返回缓存值
}
输出:
Before accessing settings
Loading settings...
Dark Mode Enabled
Dark Mode Enabled
📌 适用场景:
计算成本较高的变量(如读取文件、数据库查询)。
仅在需要时才进行初始化,避免不必要的资源消耗。
4. 自定义 getter 和 setter
自定义 getter
如果希望在每次访问属性时动态计算其值,可以使用 get() 方法:
class Rectangle(val width: Int, val height: Int) {
val area: Int
get() = width * height // 计算面积
}
fun main() {
val rect = Rectangle(5, 10)
println(rect.area) // 输出:50
}
自定义 setter
set() 允许在属性赋值时执行额外的逻辑,例如数据校验或触发某些操作:
class User {
var age: Int = 0
set(value) {
if (value < 0) {
throw IllegalArgumentException("Age cannot be negative")
}
field = value // `field` 代表实际存储的值
}
}
fun main() {
val user = User()
user.age = 25 // ✅ 正常赋值
println(user.age)
user.age = -5 // ❌ 抛出异常
}
📌 注意:
field 关键字用于访问当前属性的真实存储值,避免无限递归。
在 Java 字节码中,Kotlin 的 field 对应的是 Java 类中的私有成员变量。
使用 javap -c Example.class 反编译 Java 字节码后,我们可以看到类似的字节码指令(简化版):
private int value = 0;
public int getValue() {
return this.value + 1;// 如果没有 `field` 关键字,则是return getValue() + 1;
}
而如果没有 field 关键字,Kotlin 代码会不断调用自身 getValue() 方法,从而导致无限递归。
5. const val(编译期常量)
const val 用于声明编译期常量,只能修饰顶层变量或 object 内部的变量,并且类型只能是基本数据类型或 String。
const val API_URL = "https://example.com/api"
fun main() {
println(API_URL)
}
❌ 错误示例:
class Config {
const val TIMEOUT = 5000 // ❌ 编译错误:const 不能用于类的成员变量
}
✅ 正确做法(放在 companion object 内):
class Config {
companion object {
const val TIMEOUT = 5000
}
}
6. companion object(伴生对象)
Kotlin 类没有 static 关键字,但可以使用 companion object 定义伴生对象,类似 Java 的 static 变量和方法。
class Database {
companion object {
val CONNECTION_TIMEOUT = 5000
}
}
fun main() {
println(Database.CONNECTION_TIMEOUT) // 直接访问
}
结论
Kotlin 提供了多种管理成员变量的方式,每种方式都有其适用场景:
|
关键字 |
适用场景 |
|
var |
声明可变变量(值可修改) |
|
val |
声明只读变量(值不可修改,类似 Java 的 final) |
|
lateinit var |
延迟初始化变量(用于非空类型且不能用于基本数据类型,如 Int、Boolean) |
|
by lazy |
惰性初始化(仅在首次访问时计算值,并缓存结果,常用于 val 属性) |
|
const val |
声明编译期常量(仅限顶层或 companion object 中,且类型需为基本类型或 String) |
|
getter/setter |
自定义属性的访问或修改逻辑(如数据验证、计算属性等) |
合理使用这些特性,可以让你的 Kotlin 代码更加简洁、高效!
更多推荐


所有评论(0)