跳到主要内容

Kotlin 构造函数

提示
  1. 构造函数类型:Kotlin 提供两种类型的构造函数,主构造函数用于简化类属性初始化,而次级构造函数允许额外的初始化逻辑。
  2. 主构造函数的使用:主构造函数是类头的一部分,可声明类属性。它支持初始化器块(init)用于放置初始化代码,并可为构造函数参数提供默认值。
  3. 次级构造函数的特点:次级构造函数使用 constructor 关键字定义,用于类扩展和多种初始化方式。它们必须直接或间接地初始化基类,并可通过 this() 调用同一类中的另一个构造函数。

构造函数是初始化类属性的简洁方式。

它是在实例化(创建)对象时调用的特殊成员函数。然而,Kotlin 中的构造函数工作方式略有不同。

在 Kotlin 中,有两种构造函数:

  • 主构造函数 - 初始化类的简洁方式
  • 次级构造函数 - 允许你添加额外的初始化逻辑

主构造函数

主构造函数是类头的一部分。这里有一个例子:

class Person(val firstName: String, var age: Int) {
// 类体
}

括号内的代码块是主构造函数:(val firstName: String, var age: Int)

构造函数声明了两个属性:firstName(只读属性,因为它使用关键字 val 声明)和 age(可读写属性,因为它使用关键字 var 声明)。

示例:主构造函数

fun main(args: Array<String>) {

val person1 = Person("Joe", 25)

println("First Name = ${person1.firstName}")
println("Age = ${person1.age}")
}

class Person(val firstName: String, var age: Int) {

}

当你运行程序时,输出将是:

First Name = Joe
Age = 25

当创建 Person 类的对象时,"Joe"25 值就好像 Person 是一个函数一样被传递。

这将 person1 对象的 firstNameage 属性分别初始化为 "Joe"25

还有其他使用主构造函数的方法。

主构造函数和初始化器块

主构造函数的语法受限,不能包含任何代码。

为了放置初始化代码(不仅是初始化属性的代码),使用初始化器块。它以 init 关键字为前缀。让我们用初始化器块修改上面的示例:

fun main(args: Array<String>) {
val person1 = Person("joe", 25)
}

class Person(fName: String, personAge: Int) {
val firstName: String
var age: Int

// 初始化器块
init {
firstName = fName.capitalize()
age = personAge

println("First Name = $firstName")
println("Age = $age")
}
}

当你运行程序时,输出将是:

First Name = Joe
Age = 25

这里,括号内的参数 fNamepersonAge 分别接受值 "Joe"25,当创建 person1 对象时。然而,fNamepersonAge 没有使用 varval,并不是 Person 类的属性。

Person 类有两个属性 firstNameage 被声明。 当创建 person1 对象时,初始化块内的代码被执行。初始化块不仅初始化其属性,还打印它们。

以下是执行相同任务的另一种方式:

fun main(args: Array<String>) {
val person1 = Person("joe", 25)
}

class Person(fName: String, personAge: Int) {
val firstName = fName.capitalize()
var age = personAge

// 初始化块
init {
println("姓名 = $firstName")
println("年龄 = $age")
}
}

为了区分构造函数参数和属性,使用了不同的名称(fNamefirstName,以及 personAgeage)。通常使用 _firstName_age 而不是为构造函数参数使用完全不同的名称。例如:

class Person(_firstName: String, _age: Int) {
val firstName = _firstName.capitalize()
var age = _age

// 初始化块
init {
... .. ...
}
}

主构造函数中的默认值

你可以为构造函数参数提供默认值(类似于为函数提供默认参数)。例如:

fun main(args: Array<String>) {

println("person1 被实例化")
val person1 = Person("joe", 25)

println("person2 被实例化")
val person2 = Person("Jack")

println("person3 被实例化")
val person3 = Person()
}

class Person(_firstName: String = "UNKNOWN", _age: Int = 0) {
val firstName = _firstName.capitalize()
var age = _age

// 初始化块
init {
println("姓名 = $firstName")
println("年龄 = $age\n")
}
}

当你运行程序时,输出将是:

姓名 = Joe
年龄 = 25

person2 被实例化
姓名 = Jack
年龄 = 0

person3 被实例化
姓名 = UNKNOWN
年龄 = 0

Kotlin 次构造函数

在 Kotlin 中,一个类也可以包含一个或多个次构造函数。它们是使用 constructor 关键字创建的。

次构造函数在 Kotlin 中不那么常见。次构造函数最常见的用途出现在你需要扩展一个提供多个初始化类的不同方式的构造函数的类时。在学习之前,请确保查看 Kotlin 继承

以下是在 Kotlin 中创建次构造函数的方式:

class Log {
constructor(data: String) {
// 一些代码
}
constructor(data: String, numberOfData: Int) {
// 一些代码
}
}

在这里,Log 类有两个次级构造函数,但没有主构造函数。

你可以这样扩展类:

class Log {
constructor(data: String) {
// 代码
}
constructor(data: String, numberOfData: Int) {
// 代码
}
}

class AuthLog: Log {
constructor(data: String): super(data) {
// 代码
}
constructor(data: String, numberOfData: Int): super(data, numberOfData) {
// 代码
}
}

这里,派生类 AuthLog 的构造函数调用基类 Log 的相应构造函数。为此,使用了 super()

从派生类调用基类的构造函数。

在 Kotlin 中,你也可以像在 Java 中一样,使用 this() 从同一类的另一个构造函数调用构造函数。

class AuthLog: Log {
constructor(data: String): this(data, 10) {
// 代码
}
constructor(data: String, numberOfData: Int): super(data, numberOfData) {
// 代码
}
}

从同一类调用构造函数

示例:Kotlin 次级构造函数

fun main(args: Array<String>) {

val p1 = AuthLog("Bad Password")
}

open class Log {
var data: String = ""
var numberOfData = 0
constructor(_data: String) {

}
constructor(_data: String, _numberOfData: Int) {
data = _data
numberOfData = _numberOfData
println("$data: $numberOfData 次")
}
}

class AuthLog: Log {
constructor(_data: String): this("来自 AuthLog -> " + _data, 10) {
}

constructor(_data: String, _numberOfData: Int): super(_data, _numberOfData) {
}
}

当你运行程序时,输出将是:

来自 AuthLog -> Bad Password: 10

注意: 如果类没有主构造函数,次级构造函数必须初始化基类或委托给另一个构造函数(如上例所示)。