Kotlin:构造器和初始化

Kotlin 中有两种构造器:主构造器和次构造器。都用关键字 constructor 来表示。但是在每个类中,主构造器有且仅有一个(如果没有定义,则编译器会自动提供一个空构造器),而次构造器可以有 0 个或多个。

主构造器声明

主构造器声明在类名后面,每个类有且仅有一个。

空参/有参主构造器

1
2
3
4
5
6
7
8
9
10
11
12
13
// 无参
class People constructor() {
var name: String? = null
var age: Int = 10
lateinit var cards: MutableList<Card>
}

// 有参
class People constructor(name: String, age: Int) {
var name: String? = null
var age: Int = 10
lateinit var cards: MutableList<Card>
}

主构造器的 constructor 关键字可以省略

1
2
3
4
5
6
7
8
9
10
11
12
13
// 无参
class People() {
var name: String? = null
var age: Int = 10
lateinit var cards: MutableList<Card>
}

// 有参
class People(name: String?, age: Int) {
var name: String? = null
var age: Int = 10
lateinit var cards: MutableList<Card>
}

当然,如果需要加权限修饰符,那么就不能省略 constructor 关键字了,而且该权限修饰符在 constructor 关键字前面。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 无参
class People private constructor() {
var name: String? = null
var age: Int = 10
lateinit var cards: MutableList<Card>
}

// 有参
class People private constructor(name: String?, age: Int) {
var name: String? = null
var age: Int = 10
lateinit var cards: MutableList<Card>
}

init 代码块

Kotlin的主构造器是没有方法体的,这就意味着我们无法在主构造器中进行任何操作。

但 Kotlin 为我们提供了一个 init 代码块,这个代码块的执行顺序在主构造方法之后,次构造方法之前,我们可以在这个代码块中进行各种初始化的操作,包括访问主构造方法中的参数。

1
2
3
4
5
6
7
8
9
10
class People(name: String?, age: Int) {
var name: String? = null
var age: Int = 10
lateinit var cards: MutableList<Card>

init {
this.name = name
this.age = age
}
}

可以看到在这里,init 代码块将属性 name 和 age 初始化了。但是,这样类似 Java 的做法看起来非常繁琐,Kotlin 提供了一个比较简洁的方法,即在主构造器中声明属性。下面的写法与上面的完全等价。

1
2
3
class People(var name: String? = null, var age: Int = 10) {
lateinit var cards: MutableList<Card>
}

当然,主构造器中声明的属性也可以加上权限修饰符。

1
2
3
class People(private var name: String? = null, private var age: Int = 10) {
lateinit var cards: MutableList<Card>
}

次构造器声明

次构造器声明在类内,每个类可以有 0 个或多个。

空参/有参次构造器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class People {
var name: String? = null
var age: Int = 10
lateinit var Cards: MutableList<Card>

// 空参次构造器
constructor() {}

// 有参次构造器
constructor(name: String?, age: Int) {
this.name = name
this.age = age
}

// 当构造器方法体内部没有逻辑的时候,可以省略大括号,即
// constructor()
// 有参构造器也可以省略没有逻辑的方法体,即
// constructor(name: String?, age: Int)
// 但是有参的空构造方法是没有意义的

// 也可以加权限修饰符
private constructor(name: String?, age: Int, cards: MutableList<Card>) {
this.name = name
this.age = age
this.cards = cards
}
}

没有主构造器时,次构造器就代替了主构造器的作用。(但是其实仍然调用了空的主构造器)

当主构造器和次构造器同时存在时,次构造器必须直接或者间接调用主构造器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class People(name: String?) {
var name: String? = null
var age: Int = 10
lateinit var cards: MutableList<Card>

// 调用了下面的有参构造器,间接地调用了主构造器
constructor() : this("你好", 100)

// 直接调用了主构造器
constructor(name: String?, age: Int) : this(name) {
this.name = name
this.age = age
}
}

可以看到,在执行顺序上,主构造器 > init代码块 > 次构造器