Kotlin 的 **init
代码块**是类初始化逻辑的核心部分,用于在对象创建时执行特定的初始化操作。以下是其核心知识点和典型用法:
基本特性
-
执行时机:
- 在类实例化时,主构造函数调用后立即执行。
- 与属性初始化器按代码中的声明顺序执行。
-
主要用途:
- 初始化需要复杂逻辑的属性。
- 验证构造函数参数的有效性。
- 执行对象创建时的必要操作(如资源加载、日志记录)。
语法结构
kotlin
class MyClass(param: String) {
// 属性初始化器(直接赋值)
val simpleProperty = param.uppercase()
// init 代码块
init {
require(param.isNotEmpty()) { "参数不能为空" } // 参数校验
println("对象已创建,参数为:$param")
}
// 另一个 init 块(按声明顺序执行)
init {
println("第二个初始化块")
}
}
执行顺序
- 主构造函数参数 → 属性初始化器 → **
init
块 → 次构造函数体**(若有)。 - 多个
init
块按代码中的声明顺序依次执行。 - 若存在次构造函数 ,所有
init
块和属性初始化器会在次构造函数体之前执行。
示例:
kotlin
class Person(val name: String) {
val firstProperty = "第一个属性".also(::println)
init {
println("第一个 init 块")
}
val secondProperty = "第二个属性".also(::println)
init {
println("第二个 init 块")
}
constructor(name: String, age: Int) : this(name) {
println("次构造函数体") // 最后执行
}
}
// 输出:
// 第一个属性
// 第一个 init 块
// 第二个属性
// 第二个 init 块
// 次构造函数体
典型使用场景
1. 参数校验
javascript
class User(name: String) {
init {
require(name.isNotBlank()) { "用户名不能为空" }
}
}
2. 复杂属性初始化
kotlin
class DatabaseConfig(url: String) {
val connectionTimeout: Int
init {
connectionTimeout = parseTimeout(url) // 调用函数计算值
}
private fun parseTimeout(url: String): Int { /* ... */ }
}
3. 资源初始化
arduino
class FileLoader(path: String) {
private val file: File
init {
file = File(path)
file.createNewFile() // 创建文件
}
}
注意事项
-
避免访问未初始化的属性:
kotlinclass Demo { val a = 1 init { println(a) // 正确:输出 1 println(b) // 错误!此时 b 尚未初始化 } val b = 2 }
-
与次构造函数的关系:
- 所有
init
块和属性初始化器会在次构造函数体之前执行。 - 次构造函数必须直接或间接委托给主构造函数。
kotlinclass Person(val name: String) { init { println("主构造逻辑") } constructor() : this("默认名称") { println("次构造逻辑") // 在 init 块之后执行 } }
- 所有
-
异常处理:
init
块中可抛出异常,中断对象创建:
arduinoclass Validator(rule: String) { init { if (rule.contains("invalid")) throw IllegalArgumentException("非法规则") } }
与 Java 初始化块的对比
这个不是Java的 static{} 相当于是Java的 {} 构造代码块
特性 | Kotlin init 块 |
Java 实例初始化块 |
---|---|---|
执行时机 | 主构造函数之后 | 构造函数之前 |
与构造函数关系 | 属于主构造函数的一部分 | 独立于构造函数 |
多个块执行顺序 | 按代码顺序执行 | 按代码顺序执行 |
参数访问 | 直接使用主构造函数参数 | 需通过构造函数参数赋值给字段 |
总结
-
使用
init
的场景:- 需要在主构造函数中嵌入复杂逻辑时。
- 确保对象创建时属性合法 或资源就绪。
-
避免
init
的场景:- 简单属性初始化(直接使用属性初始化器)。
- 与次构造函数逻辑高度耦合时(优先将逻辑放在次构造函数体内)