7.2 类与构造函数

Kotlin和Java很相似,也是一种面向对象的语言。下面我们来一起学习Kotlin的面向对象的特性。如果您熟悉Java或者C++、C#中的类,您可以很快上手。同时,您也将看到Kotlin与Java中的面向对象编程的一些不同的特性。

Kotlin中的类和接口跟Java中对应的概念有些不同,比如接口可以包含属性声明;Kotlin的类声明,默认是final和public的。

另外,嵌套类并不是默认在内部的。它们不包含外部类的隐式引用。

在构造函数方面,Kotlin简短的主构造函数在大多数情况下都可以满足使用,当然如果有稍微复杂的初始化逻辑,我们也可以声明次级构造函数来完成。

我们还可以使用 data 修饰符来声明一个数据类,使用 object 关键字来表示单例对象、伴生对象等。

Kotlin类的成员可以包含:

  • 构造函数和初始化块
  • 属性
  • 函数
  • 嵌套类和内部类
  • 对象声明

7.2.1 声明类

和大部分语言类似,Kotlin使用class作为类的关键字,当我们声明一个类时,直接通过class加类名的方式来实现:

1. class World

这样我们就声明了一个World类。

7.2.2 构造函数

在 Kotlin 中,一个类可以有

  • 一个主构造函数(primary constructor)
  • 一个或多个次构造函数(secondary constructor)

主构造函数

主构造函数是类头的一部分,直接放在类名后面:

1. open class Student constructor(var name: String, var age: Int) : Any() {
2. ...
3. }

如果主构造函数没有任何注解或者可见性修饰符,可以省略这个 constructor 关键字。如果构造函数有注解或可见性修饰符,这个 constructor 关键字是必需的,并且这些修饰符在它前面:

1. annotation class MyAutowired

3. class ElementaryStudent public @MyAutowired constructor(name: String, age: Int) : Student(name, age) {
4. ...
5. }

与普通属性一样,主构造函数中声明的属性可以是可变的(var)或只读的(val)。

主构造函数不能包含任何的代码。初始化的代码可以放到以 init 关键字作为前缀的初始化块(initializer blocks)中:

1. open class Student constructor(var name: String, var age: Int) : Any() {

3. init {
4. println("Student{name=$name, age=$age} created!")
5. }
6. ...

8. }

主构造的参数可以在初始化块中使用,也可以在类体内声明的属性初始化器中使用。

次构造函数

在类体中,我们也可以声明前缀有 constructor 的次构造函数,次构造函数不能有声明 val 或 var :

1. class MiddleSchoolStudent {
2. constructor(name: String, age: Int) {
3. }
4. }

如果类有一个主构造函数,那么每个次构造函数需要委托给主构造函数, 委托到同一个类的另一个构造函数用 this 关键字即可:

1. class ElementarySchoolStudent public @MyAutowired constructor(name: String, age: Int) : Student(name, age) {
2. override var weight: Float = 80.0f

4. constructor(name: String, age: Int, weight: Float) : this(name, age) {
5. this.weight = weight
6. }

8. ...
9. }

如果一个非抽象类没有声明任何(主或次)构造函数,它会有一个生成的不带参数的主构造函数。构造函数的可见性是 public。

私有主构造函数

我们如果希望这个构造函数是私有的,我们可以如下声明:

1. class DontCreateMe private constructor() {
2. }

这样我们在代码中,就无法直接使用主构造函数来实例化这个类,下面的写法是不允许的:

1. val dontCreateMe = DontCreateMe() // cannot access it

但是,我们可以通过次构造函数引用这个私有主构造函数来实例化对象:

7.2.2 类的属性

我们再给这个World类加入两个属性。我们可能直接简单地写成:

1. class World1 {
2. val yin: Int
3. val yang: Int
4. }

在Kotlin中,直接这样写语法上是会报错的:

Kotlin极简教程

意思很明显,是说这个类的属性必须要初始化,或者如果不初始化那就得是抽象的abstract属性。

我们把这两个属性都给初始化如下:

1. class World1 {
2. val yin: Int = 0
3. val yang: Int = 1
4. }

我们再来使用测试代码来看下访问这两个属性的方式:

1. >>> class World1 {
2. ...     val yin: Int = 0
3. ...     val yang: Int = 1
4. ... }
5. >>> val w1 = World1()
6. >>> w1.yin
7. 0
8. >>> w1.yang
9. 1

上面的World1类的代码,在Java中等价的写法是:

1. public final class World1 {
2. private final int yin;
3. private final int yang = 1;

5. public final int getYin() {
6. return this.yin;
7. }

9. public final int getYang() {
10. return this.yang;
11. }
12. }

我们可以看出,Kotlin中的类的字段自动带有getter方法和setter方法。而且写起来比Java要简洁的多。

7.2.3 函数(方法)

我们再来给这个World1类中加上一个函数:

1. class World2 {
2. val yin: Int = 0
3. val yang: Int = 1

5. fun plus(): Int {
6. return yin + yang
7. }
8. }

10. val w2 = World2()
11. println(w2.plus()) // 输出 1