在 Kotlin 中, open 关键字表示一个类或一个成员 (函数或属性) 在子类中能够被覆盖. 默认情况下, Kotlin 类及其成员都是 final 的, 也就是说, 除非你明确的将它们标记为 open, 否则类不能被继承, 成员不能被覆盖:
// 带有 open 关键字的基类, 允许继承
open class Person(
val name: String,
) {
// open 的函数, 在子类中能够被覆盖
open fun introduce() {
println("Hello, my name is $name.")
}
}
// 子类继承自 Person, 并覆盖 introduce() 函数
class Student(
name: String,
val school: String,
) : Person(name) {
override fun introduce() {
println("Hi, I'm $name, and I study at $school.")
}
}
如果你覆盖基类的一个成员, 那么覆盖后的成员默认也是 open 的. 如果你想要修改这个行为, 禁止你的类的子类覆盖你的实现, 你可以将覆盖后的成员明确的标记为 final:
// 带有 open 关键字的基类, 允许继承
open class Person(
val name: String,
) {
// open 的函数, 在子类中能够被覆盖
open fun introduce() {
println("Hello, my name is $name.")
}
}
// 子类继承自 Person, 并覆盖 introduce() 函数
class Student(
name: String,
val school: String,
) : Person(name) {
// final 关键字禁止子类中进一步覆盖
final override fun introduce() {
println("Hi, I'm $name, and I study at $school.")
}
}
open class Shape {
open val vertexCount: Int = 0
}
class Rectangle : Shape() {
override val vertexCount = 4
}
你也可以使用一个 var 属性覆盖一个 val 属性, 但不可以反过来使用一个 val 属性覆盖一个 var 属性. 允许这种覆盖的原因是, val 属性本质上只是定义了一个 get 方法, 使用 var 属性来覆盖它, 只是向后代类中添加了一个 set 方法.
注意, 你可以在主构造器的属性声明中使用 override 关键字:
interface Shape {
val vertexCount: Int
}
class Rectangle(override val vertexCount: Int = 4) : Shape // 长方形总是拥有 4 个顶点
class Polygon : Shape {
override var vertexCount: Int = 0 // 多边形的顶点数目不定, 可以变更为任何数字
}
//sampleStart
open class Base(val name: String) {
init { println("Initializing a base class") }
open val size: Int =
name.length.also { println("Initializing size in the base class: $it") }
}
class Derived(
name: String,
val lastName: String,
) : Base(name.replaceFirstChar { it.uppercase() }.also { println("Argument for the base class: $it") }) {
init { println("Initializing a derived class") }
override val size: Int =
(super.size + lastName.length).also { println("Initializing size in the derived class: $it") }
}
//sampleEnd
fun main() {
println("Constructing the derived class(\"hello\", \"world\")")
Derived("hello", "world")
}
也就是说, 基类构造器执行时, 在子类中定义或覆盖的属性还没有被初始化. 如果在基类初始化逻辑中使用到这些属性 (无论是直接使用, 还是通过另一个被覆盖的 open 成员间接使用), 可能会导致不正确的行为, 甚至导致运行时错误. 因此, 设计基类时, 在构造器, 属性初始化器, 以及 init 代码段中, 你应该避免使用 open 成员.
调用超类中的实现
后代类中的代码, 可以使用 super 关键字来调用超类中的函数和属性访问器的实现:
open class Rectangle {
open fun draw() { println("Drawing a rectangle") }
val borderColor: String get() = "black"
}
class FilledRectangle : Rectangle() {
override fun draw() {
super.draw()
println("Filling the rectangle")
}
val fillColor: String get() = super.borderColor
}
在内部类(inner class)的代码中, 可以使用 super 关键字加上外部类名称限定符: super@Outer 来访问外部类(outer class)的超类:
open class Rectangle {
open fun draw() { println("Drawing a rectangle") }
val borderColor: String get() = "black"
}
//sampleStart
class FilledRectangle: Rectangle() {
override fun draw() {
val filler = Filler()
filler.drawAndFill()
}
inner class Filler {
fun fill() { println("Filling") }
fun drawAndFill() {
super@FilledRectangle.draw() // 调用 Rectangle 的 draw() 函数实现
fill()
println("Drawn a filled rectangle with color ${super@FilledRectangle.borderColor}") // 使用 Rectangle 的 borderColor 属性的 get() 函数
}
}
}
//sampleEnd
fun main() {
val fr = FilledRectangle()
fr.draw()
}