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 教程。