1. 概述

在本教程中,我们将深入探讨 Kotlin 中的构造函数。

首先快速回顾一下概念:构造函数用于创建对象。它们看起来像方法声明,但名称始终与类名一致,并且不返回任何值。

如果你需要搭建 Kotlin 项目,可以参考我们的 Kotlin 入门指南

在 Kotlin 中,一个类可以有一个主构造函数(primary constructor)和一个或多个次构造函数(secondary constructor)

接下来的章节中,我们会逐一介绍每种构造函数及其相关特性。

2. 主构造函数(Primary Constructor)

Kotlin 中创建对象的第一种方式就是使用主构造函数。

⚠️ 主构造函数是类头的一部分。参数也可以直接作为类的属性,紧跟在类声明之后。

来看一个简单的类定义示例,包含两个属性和一个主构造函数:

class Person constructor(val name: String, val age: Int? = null)

在这个例子中,我们通过 val 关键字声明了属性。这和普通变量的行为类似,只不过 val 声明的是只读属性(相当于 Java 中的 final)。

如果后续需要修改引用,应该使用 var 关键字。不过,主构造函数中的字段类型不能省略,必须显式声明

⚠️ 在某些情况下,我们可以省略 constructor 关键字。只有在使用注解(如 @Autowired)或访问修饰符(如 privateprotected)时,才必须显式写出。

此外,Kotlin 的主构造函数支持默认参数。

接下来,看看如何使用主构造函数:

val person = Person("John")
val personWithAge = Person("Mark", 22)

可以看到,类名就是构造函数调用,无需使用 new 关键字

作为对比,下面是 Java 中等价的构造函数实现:

class PersonJava {
    final String name;
    final Integer age;

    public PersonJava(String name) {
        this.name = name;
        this.age = null;
    }

    public PersonJava(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

显然,Java 要实现相同的功能需要更多的代码。

2.1. JVM 上的主构造函数

请注意,编译器会在 JVM 上生成一个无参构造函数,用于初始化默认值。

这样,Kotlin 就能很好地与 Jackson、JPA 等依赖无参构造函数来创建类实例的库配合使用:

class Person(val name: String = "")

3. 从构造参数初始化属性

类体中的属性初始化器可以使用主构造函数的参数

比如,将 name 转换为大写形式的 upperCaseName 属性:

class Person(val name: String, val age: Int? = null) {
    val upperCaseName: String = name.toUpperCase()
}

我们可以通过添加 init 块来在控制台输出结果:

init {
    println("Upper case name is $upperCaseName")
}

4. 初始化块(Initializer Blocks)

⚠️ 主构造函数中不能直接写执行代码

但有时我们需要执行一些初始化逻辑,这时候就可以使用初始化块,以 init 关键字开头。

初始化块在主构造函数之后执行,并且可以访问类的字段。

一个类可以有多个 init 块。

Person 类添加一个初始化块:

init {
    println("Hello, I'm $name")        
    if (surname.isEmpty()) {
        throw IllegalArgumentException("Surname cannot be empty!")
    }
}

创建 Person 对象时,控制台将输出:

Hello, I'm John

如果 surname 为空,会抛出 IllegalArgumentException 异常。

多个 init 块会按照它们在类体中出现的顺序依次执行。

5. 次构造函数(Secondary Constructor)

在 Kotlin 类中,我们还可以定义一个或多个次构造函数。✅ 次构造函数以 constructor 关键字开头

class Car {
    val id: String
    val type: String

    constructor(id: String, type: String) {
        this.id = id
        this.type = type
    }
}

使用方式如下:

fun main(args: Array<String>) {
    val car = Car("1", "sport")
    val suvCar = Car("2", "suvCar")
}

⚠️ 每个次构造函数都必须委托给主构造函数,通过 this 关键字实现。

我们将属性移到主构造函数中,修改次构造函数如下:

class Car(val id: String, val type: String) {
    constructor(id: String): this(id, "unknown")
}

6. 构造函数与继承

我们可以使用父类的主构造函数。

注意,Kotlin 中所有类默认是 final 的。因此,如果希望某个类能被继承,需要加上 open 关键字。

比如定义一个继承自 PersonEmployee 类,两者都使用主构造函数:

class Employee(name: String, val salary: Int): Person(name)

这样我们就把 name 传递给了 Person 的主构造函数,同时在 Employee 中新增了 salary 字段。

7. 总结

在这篇文章中,我们探讨了 Kotlin 中构造函数的各种用法。根据需求,我们可以灵活地初始化字段。

所有示例的完整实现可以在 GitHub 项目 中找到。

如需了解更多 Kotlin 特性,请查看我们的 Kotlin 入门指南


原始标题:Kotlin Constructors

« 上一篇: Future 的同步处理