Kotlin之【init】—— 新手须知

在 Kotlin 中,init 块是初始化块,用于在类实例创建时执行额外的初始化逻辑。它属于类的主构造函数的一部分,在属性初始化器和主构造函数的参数之后、次构造函数的主体之前执行。

1. 基本语法

kotlin 复制代码
class MyClass {
    init {
        // 初始化代码
    }
}

可以定义多个 init 块,它们按照在类中出现的从上到下的顺序依次执行。

2. 执行时机与顺序

一个完整的初始化顺序是:

  1. 主构造函数的参数(仅作为形参,未转化为属性时)
  2. 类中定义的属性初始化器 (例如 val a = 10
  3. init(若有多个,依次执行)
  4. 次构造函数的主体(如果通过次构造函数创建实例,且该构造器最终委托给主构造器)

示例:

kotlin 复制代码
class Person(name: String, age: Int) {
    val upperName = name.uppercase()   // ① 属性初始化

    init {
        println("第一个 init 块: name=$name, age=$age")
    }

    val greeting = "Hello, $upperName" // ② 另一个属性初始化

    init {
        println("第二个 init 块: greeting=$greeting")
    }

    constructor(name: String) : this(name, 0) {
        println("次构造函数主体")       // ③ 在 init 之后执行
    }
}

fun main() {
    val p = Person("Alice", 25)
}

输出:

text 复制代码
第一个 init 块: name=Alice, age=25
第二个 init 块: greeting=Hello, ALICE

如果使用次构造函数:Person("Bob"),输出会额外增加最后一行"次构造函数主体"。

3. 主要用途

  • 参数验证:检查传递给构造函数的参数合法性。
  • 进行无法在属性初始化器中完成的复杂计算
  • 调用非 Kotlin 标准库的 API(如 Android 中的视图设置)
  • 在多个构造函数间共享初始化逻辑 (因为所有次构造函数最终都会委托给主构造器,init 块一定会执行)。

示例:参数验证

kotlin 复制代码
class User(email: String, password: String) {
    val email: String
    val password: String

    init {
        require(email.isNotBlank()) { "Email cannot be blank" }
        require(password.length >= 6) { "Password too short" }
        this.email = email.trim()
        this.password = password
    }
}

4. 与属性声明的关系

由于 init 块在属性初始化器之后执行,因此可以在 init 块中访问已经初始化的属性。但需要注意声明的顺序 :如果在 init 块中访问一个尚未初始化的属性(即属性初始化器在 init 块之后),编译器会报错。

kotlin 复制代码
class Demo {
    init {
        println(a)  // 错误:变量 'a' 必须被初始化
    }
    val a = 10
}

正确的做法是将 init 块放在需要访问的属性之后,或者保证该属性有默认值。

5. 与主构造函数参数的交互

主构造函数的参数可以在属性初始化器和 init 块中直接使用(前提是它们没有被 var/val 修饰成属性时,作用域仅限于初始化阶段)。如果想在类的方法中也使用参数,需要将参数声明为属性(val/var)。

kotlin 复制代码
class Rectangle(val width: Int, val height: Int) {
    val area: Int

    init {
        area = width * height   // width, height 可以直接使用
    }
}

6. 多个 init 块与属性初始化器的混合顺序

所有属性初始化器和 init 块共同形成初始化序列,顺序由代码中出现的位置决定。

kotlin 复制代码
class Example {
    val first = "First".also(::println)  // ①

    init {
        println("init 1")                // ②
    }

    val second = "Second".also(::println) // ③

    init {
        println("init 2")                // ④
    }
}
// 输出: First -> init 1 -> Second -> init 2

7. 注意事项

  • init 块不是构造函数,它不能拥有自己的参数,也不能被重载。
  • 如果一个类没有显式主构造函数(即只有次构造器),但仍然可以定义 init 块。此时没有主构造器的参数可用,但 init 块仍会在次构造函数委托给主构造函数时(隐式存在)执行。
  • init 块中可以使用 return,但只是提前退出块,不会阻止实例的创建(除非抛出异常)。

8. 与 Java 构造代码块对比

特性 Kotlin init Java 实例初始化块
执行时机 属性初始化后,构造器主体前 属性初始化后,构造器主体前
是否可有多个
是否可见参数 可访问主构造参数 只能访问构造器参数(通过构造器传递)
使用场景 与主构造器紧密集成 与所有构造器共享代码

总结

  • init 块是 Kotlin 类初始化阶段的一部分,用于执行主构造函数的额外逻辑。
  • 它们按照定义顺序与属性初始化器交错执行。
  • 适用于参数验证、复杂初始化、以及在主构造器上下文中必须执行的代码。
  • 合理使用 init 块能让类初始化过程更加清晰和模块化。
相关推荐
惟恋惜1 小时前
第 3 章 · 列表、滚动与 Lazy 深入:告别 RecyclerView 思维
ui·android jetpack
古法安卓2 小时前
Android-休眠唤醒后onLocationChanged没有数据问题排查
android·java·android studio
Htr_5 小时前
Creem 2.0 使用指南:面向 AI 构建时代的资金平台
android·数据库·人工智能·ui·photoshop
wardenlzr6 小时前
车机看门狗(Watchdog)为何会让整机硬重启
android·优化·车机
IT毕设实战小研7 小时前
基于大数据的商场商铺数据分析与可视化的设计与实现
android·java·大数据·django·课程设计
aqi007 小时前
一文读懂 HarmonyOS 7.0 带来的十大API重要升级
android·华为·harmonyos·鸿蒙·harmony
是店小二呀8 小时前
鸿蒙PC开源移植:CodeLite原生IDE与Remote Agent适配
android·智能手机·远程桌面
不要喷香水8 小时前
00-总览与架构
kotlin·app·档案管理
终端安全笔记9 小时前
iOS 27 给了租赁一个新工具,但它只认受监督的设备
android·网络·安全·ios