ZetCode

Kotlin init 关键字

最后修改于 2025 年 4 月 19 日

Kotlin 的初始化系统提供了强大的方法来设置类实例。init 关键字创建初始化块,这些块在创建对象时运行。本教程通过实际示例深入探讨了 init 关键字。

基本定义

Kotlin 中的 init 关键字定义了初始化块。这些块在创建类实例时执行,在主构造函数执行之后但在次构造函数之前。它们有助于设置初始状态和验证属性。

基本 init 块

最简单的初始化块形式在创建对象时运行代码。此示例显示了一个基本的 init 块,在初始化期间打印一条消息。

BasicInit.kt
package com.zetcode

class Person {
    init {
        println("Person instance created")
    }
}

fun main() {
    val person = Person() // Output: Person instance created
}

在这里,我们定义了一个 Person 类,其中包含一个 init 块。当我们创建一个 Person 实例时,初始化块会自动执行。这对于应该在每次创建对象时运行的设置任务很有用。

带主构造函数的 init

初始化块可以访问在主构造函数中定义的属性。它们在处理完主构造函数参数后但在任何次构造函数之前执行。

PrimaryConstructorInit.kt
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 块。它们按照在类定义中出现的顺序从上到下执行。这允许将初始化逻辑组织到逻辑部分中。

MultipleInitBlocks.kt
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 中的属性初始化

初始化块可用于设置需要复杂逻辑的属性。当属性依赖于构造函数参数或需要验证时,这尤其有用。

PropertyInit.kt
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

初始化块在次构造函数体之前执行。这确保了先进行通用初始化,而次构造函数可以提供额外的设置。

SecondaryConstructorInit.kt
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 块之前运行,确保正确的初始化顺序。

InheritanceInit.kt
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 的。这确保了父类的设置在子类初始化开始之前完成。

复杂的初始化

初始化块可以包含复杂的逻辑,包括控制流和函数调用。此示例演示了在初始化期间验证和转换输入。

ComplexInit.kt
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 块的最佳实践

来源

Kotlin 构造函数文档

本教程深入介绍了 Kotlin 的 init 关键字,展示了初始化块如何与主构造函数和次构造函数一起工作。我们探讨了各种场景,包括属性初始化、继承和复杂的设置。正确使用 init 块有助于确保对象以有效状态开始。

作者

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

列出 所有 Kotlin 教程