Kotlin init 关键字
最后修改于 2025 年 4 月 19 日
Kotlin 的初始化系统提供了强大的方法来设置类实例。init 关键字创建初始化块,这些块在创建对象时运行。本教程通过实际示例深入探讨了 init 关键字。
基本定义
Kotlin 中的 init 关键字定义了初始化块。这些块在创建类实例时执行,在主构造函数执行之后但在次构造函数之前。它们有助于设置初始状态和验证属性。
基本 init 块
最简单的初始化块形式在创建对象时运行代码。此示例显示了一个基本的 init 块,在初始化期间打印一条消息。
package com.zetcode
class Person {
init {
println("Person instance created")
}
}
fun main() {
val person = Person() // Output: Person instance created
}
在这里,我们定义了一个 Person 类,其中包含一个 init 块。当我们创建一个 Person 实例时,初始化块会自动执行。这对于应该在每次创建对象时运行的设置任务很有用。
带主构造函数的 init
初始化块可以访问在主构造函数中定义的属性。它们在处理完主构造函数参数后但在任何次构造函数之前执行。
package com.zetcode
class Person(val name: String) {
init {
println("Person $name created")
}
}
fun main() {
val person = Person("John") // Output: Person John created
}
此示例显示了一个 init 块,它访问来自主构造函数的 name 属性。初始化块在分配 name 属性后但在构造函数完成之前运行。
多个 init 块
一个类可以有多个 init 块。它们按照在类定义中出现的顺序从上到下执行。这允许将初始化逻辑组织到逻辑部分中。
package com.zetcode
class Person(val name: String) {
init {
println("First init block: $name")
}
init {
println("Second init block: ${name.uppercase()}")
}
}
fun main() {
val person = Person("Alice")
// Output:
// First init block: Alice
// Second init block: ALICE
}
在这里,我们定义了两个初始化块,它们顺序执行。第一个打印名称,第二个打印大写版本。多个块有助于组织复杂的初始化逻辑。
init 中的属性初始化
初始化块可用于设置需要复杂逻辑的属性。当属性依赖于构造函数参数或需要验证时,这尤其有用。
package com.zetcode
class Circle(radius: Double) {
val area: Double
init {
require(radius > 0) { "Radius must be positive" }
area = Math.PI * radius * radius
}
}
fun main() {
val circle = Circle(5.0)
println("Area: ${circle.area}") // Output: Area: 78.53981633974483
// val invalid = Circle(-1.0) // Throws IllegalArgumentException
}
此示例显示了一个 init 块,用于验证半径并计算面积。require 函数确保半径为正数。area 属性在块中初始化。
带次构造函数的 init
初始化块在次构造函数体之前执行。这确保了先进行通用初始化,而次构造函数可以提供额外的设置。
package com.zetcode
class Person(val name: String) {
var age: Int = 0
init {
println("Primary initialization: $name")
}
constructor(name: String, age: Int) : this(name) {
this.age = age
println("Secondary constructor: $name, $age")
}
}
fun main() {
val person1 = Person("Bob")
// Output: Primary initialization: Bob
val person2 = Person("Alice", 30)
// Output:
// Primary initialization: Alice
// Secondary constructor: Alice, 30
}
在这里,init 块在次构造函数体之前运行。首先通过 this(name) 调用主构造函数,然后执行 init 块,最后运行次构造函数体。
继承中的 init
在继承层次结构中,初始化块按父到子的顺序执行。父类 init 块在子类 init 块之前运行,确保正确的初始化顺序。
package com.zetcode
open class Animal {
init {
println("Animal initialized")
}
}
class Dog : Animal() {
init {
println("Dog initialized")
}
}
fun main() {
val dog = Dog()
// Output:
// Animal initialized
// Dog initialized
}
创建 Dog 实例时,Animal 的初始化块首先运行,然后是 Dog 的。这确保了父类的设置在子类初始化开始之前完成。
复杂的初始化
初始化块可以包含复杂的逻辑,包括控制流和函数调用。此示例演示了在初始化期间验证和转换输入。
package com.zetcode
class User(email: String) {
val username: String
val domain: String
init {
require(email.contains("@")) { "Invalid email format" }
val parts = email.split("@")
username = parts[0]
domain = parts[1].lowercase()
println("User created: $username@$domain")
}
}
fun main() {
val user = User("Admin@Example.COM")
// Output: User created: Admin@example.com
println(user.username) // Output: Admin
println(user.domain) // Output: example.com
// val invalid = User("no-at-sign") // Throws IllegalArgumentException
}
此 init 块验证电子邮件格式,将其拆分为多个部分,并规范化域。复杂的初始化确保 User 对象以有效状态开始,并具有格式正确的属性。
init 块的最佳实践
- 保持专注:每个 init 块都应处理初始化的一个特定方面。
- 尽早验证:使用 init 块立即验证构造函数参数。
- 避免复杂的逻辑:如果初始化变得过于复杂,请将其移至单独的方法中。
- 考虑顺序:记住 init 块在次构造函数之前执行,并按父到子的顺序执行。
- 记录假设:在你的 init 块中清楚地记录任何重要的前提条件。
来源
本教程深入介绍了 Kotlin 的 init 关键字,展示了初始化块如何与主构造函数和次构造函数一起工作。我们探讨了各种场景,包括属性初始化、继承和复杂的设置。正确使用 init 块有助于确保对象以有效状态开始。
作者
列出 所有 Kotlin 教程。