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加类名的方式来实现:
class World
这样我们就声明了一个World类。
7.2.2 构造函数
在 Kotlin 中,一个类可以有
- 一个主构造函数(primary constructor)
- 一个或多个次构造函数(secondary constructor)
主构造函数
主构造函数是类头的一部分,直接放在类名后面:
open class Student constructor(var name: String, var age: Int) : Any() {
...
}
如果主构造函数没有任何注解或者可见性修饰符,可以省略这个 constructor 关键字。如果构造函数有注解或可见性修饰符,这个 constructor 关键字是必需的,并且这些修饰符在它前面:
annotation class MyAutowired
class ElementaryStudent public @MyAutowired constructor(name: String, age: Int) : Student(name, age) {
...
}
与普通属性一样,主构造函数中声明的属性可以是可变的(var)或只读的(val)。
主构造函数不能包含任何的代码。初始化的代码可以放到以 init 关键字作为前缀的初始化块(initializer blocks)中:
open class Student constructor(var name: String, var age: Int) : Any() {
init {
println("Student{name=$name, age=$age} created!")
}
...
}
主构造的参数可以在初始化块中使用,也可以在类体内声明的属性初始化器中使用。
次构造函数
在类体中,我们也可以声明前缀有 constructor 的次构造函数,次构造函数不能有声明 val 或 var :
class MiddleSchoolStudent {
constructor(name: String, age: Int) {
}
}
如果类有一个主构造函数,那么每个次构造函数需要委托给主构造函数, 委托到同一个类的另一个构造函数用 this 关键字即可:
class ElementarySchoolStudent public @MyAutowired constructor(name: String, age: Int) : Student(name, age) {
override var weight: Float = 80.0f
constructor(name: String, age: Int, weight: Float) : this(name, age) {
this.weight = weight
}
...
}
如果一个非抽象类没有声明任何(主或次)构造函数,它会有一个生成的不带参数的主构造函数。构造函数的可见性是 public。
私有主构造函数
我们如果希望这个构造函数是私有的,我们可以如下声明:
class DontCreateMe private constructor() {
}
这样我们在代码中,就无法直接使用主构造函数来实例化这个类,下面的写法是不允许的:
val dontCreateMe = DontCreateMe() // cannot access it
但是,我们可以通过次构造函数引用这个私有主构造函数来实例化对象:
7.2.2 类的属性
我们再给这个World类加入两个属性。我们可能直接简单地写成:
class World1 {
val yin: Int
val yang: Int
}
在Kotlin中,直接这样写语法上是会报错的:
意思很明显,是说这个类的属性必须要初始化,或者如果不初始化那就得是抽象的abstract属性。
我们把这两个属性都给初始化如下:
class World1 {
val yin: Int = 0
val yang: Int = 1
}
我们再来使用测试代码来看下访问这两个属性的方式:
>>> class World1 {
... val yin: Int = 0
... val yang: Int = 1
... }
>>> val w1 = World1()
>>> w1.yin
0
>>> w1.yang
1
上面的World1类的代码,在Java中等价的写法是:
public final class World1 {
private final int yin;
private final int yang = 1;
public final int getYin() {
return this.yin;
}
public final int getYang() {
return this.yang;
}
}
我们可以看出,Kotlin中的类的字段自动带有getter方法和setter方法。而且写起来比Java要简洁的多。
7.2.3 函数(方法)
我们再来给这个World1类中加上一个函数:
class World2 {
val yin: Int = 0
val yang: Int = 1
fun plus(): Int {
return yin + yang
}
}
val w2 = World2()
println(w2.plus()) // 输出 1