类
在 Kotlin 中,类用关键字 class
声明:
kotlin
class Person { /*...*/ }
类的声明由:类名、类头信息(指定其类型参数、主构造函数以及其他一些内容)和由花括号包裹的类体。类头和类体都是可选的;如果没有类体,那么花括号可以省略:
kotlin
class Empty
构造函数
在 Kotlin 中,一个类会有一个主构造函数和一个或者多个次要构造函数。主构造函数需要写在类头上,并且主构造函数需要写在类名后面,主构造函数还可以携带参数:
javascript
class Person constructor(firstName: String) { /*...*/ }
如果主构造函数没有注解或者可见性标识符,那么constructor
可以省略:
kotlin
class Person(firstName: String) { /*...*/ }
主构造函数初始化类实例和类属性。类头不能包含可执行的代码。开发者可以使用初始化代码块,来在对象创建期间,执行一些代码。初始化代码块使用 init
声明,初始化代码块需要包裹在花括号之中。
During the initialization of an instance, the initializer blocks are executed in the same order as they appear in the class body, interleaved with the property initializers: 初始化代码块会按着自上而下的顺序执行,所以可能属性的赋值也会在代码块改变。
kotlin
class InitOrderDemo(name: String) {
val firstProperty = "First property: $name".also(::println)
init {
println("First initializer block that prints $name")
}
val secondProperty = "Second property: ${name.length}".also(::println)
init {
println("Second initializer block that prints ${name.length}")
}
}
// 先给 firstProperty 赋值,
// 执行第一个init
// 再给secondProperty 赋值
// 执行第二个init
主构造函数的参数可以用在初始化代码块中。它们也可以用在代码体中的属性初始化中:
kotlin
class Customer(name: String) {
val customerKey = name.uppercase()
}
Kotlin 中有语法糖来实现声明和初始化属性:
kotlin
class Person(val firstName: String, val lastName: String, var age: Int)
声明也可以包含默认的值:
kotlin
class Person(val firstName: String, val lastName: String, var isEmployed: Boolean = true)
在声明类的属性时,可以使用末尾逗号:
kotlin
class Person(
val firstName: String,
val lastName: String,
var age: Int, // trailing comma
) { /*...*/ }
和常规属性类似,主函数中的属性可以是 var
或者 val
Plain constructor parameters (that are not properties) are accessible in: 未用var
或者 val
修饰的构造参数在以下场景是可用的:
- 类头
- 类体中的已初始化属性
- 初始化代码块
它们仅在初始化阶段有效,不成为类的成员,因此无法在类的方法中直接访问。
kotlin
class RectangleWithParameters(width: Int, height: Int) {
val perimeter = 2 * width + 2 * height
init {
println("Rectangle created with width = $width and height = $height")
}
}

如果构造函数有注解或者可见性修饰符,那么 constructor
关键字是必须的,并且修饰符在 constructor
之前:
kotlin
class Customer public @Inject constructor(name: String) { /*...*/ }
次构造函数
类也可以声明次要构造函数,次要构造函数使用 constructor
声明:
kotlin
class Person(val pets: MutableList<Pet> = mutableListOf())
class Pet {
constructor(owner: Person) {
owner.pets.add(this) // adds this pet to the list of its owner's pets
}
}
如果类本身有一个主构造函数,那么每个次要函数需要代理到主构造函数,要么直接调用,要么通过其他的次要函数调用。如果代理到一个类的其他构造函数,就可以使用 this
关键字:
kotlin
class Person(val name: String) {
val children: MutableList<Person> = mutableListOf()
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
初始化代码块实际上是主构造方法的一部分。代理到主构造函数的时机是访问次要构造函数第一句的时候,因此,所有的初始化代码块和初始化属性会在次要构造函数体之前调用。
即使类没有主构造函数,代理也会隐式发生,并且所有的初始化代码块依然会执行:
kotlin
class Constructors {
init {
println("Init block")
}
constructor(i: Int) {
println("Constructor $i")
}
}
// first
// first block
// second
// second block
// Constructor 1
即使一个非抽象类没有声明构造函数(主要和次要),这个类也会生成一个无参的主要构造,并且这个构造是 public 的。
如果开发者不想要有一个 public 的构造,那么可以声明一个空的非默认可见性的主要构造:
kotlin
class DontCreateMe private constructor() { /*...*/ }
创建类实例
开发者可以调用构造函数来实例化一个类,将实例化的对象来给变量赋值:
kotlin
val invoice = Invoice()
val customer = Customer("Joe Smith")
类成员
类可以包含:
- 构造方法和初始化代码块
- 方法
- 属性
- 嵌套类和内部类
- 对象声明
继承
类之间可以相互派生,形成继承层次结构。
抽象类
一个类可以被声明成抽象类,抽象类的部分或者全部成员可以是抽象的。一个抽象成员在类中是没有具体实现的。开发者不需要使用 open
来注解一个抽象类或者抽象方法。
kotlin
abstract class Polygon {
abstract fun draw()
}
class Rectangle : Polygon() {
override fun draw() {
// draw the rectangle
}
}
开发者可以使用抽象来重写一个非抽象的open
成员。
kotlin
open class Polygon {
open fun draw() {
// some default polygon drawing method
}
}
abstract class WildShape : Polygon() {
// Classes that inherit WildShape need to provide their own
// draw method instead of using the default on Polygon
abstract override fun draw()
}
伴生对象
开发者可以在类内容声明一个伴生对象,这样就可以在不创建对象的情况下,使用伴生对象访问类的成员。