# 继承 > **Tip:** > 在创建类的继承层级结构之前, 请考虑使用 [抽象类](classes.html#abstract-classes) 或 [接口](interfaces.html). 默认情况下, 你可以从抽象类和接口继承. 它们的目的就是为了让其它类能够继承并实现它们的成员. Kotlin 中所有的类都有一个共同的超类 `Any`, 如果类声明时没有指定超类, 则默认为 `Any`: ```KOTLIN class Example // 隐含地继承自 Any ``` `Any` 拥有三个函数: `equals()`, `hashCode()` 和 `toString()`. 因此, Kotlin 的所有类都拥有这些函数. 默认情况下, Kotlin 的类是 final 的 - 不能再被继承. 如果要允许一个类被继承, 需要使用 `open` 关键字标记这个类: ```KOTLIN open class Base // 这个类现在是 open 的, 可以被继承 ``` [详情请参见 open 关键字](#open-keyword). 要明确声明类的超类, 要在类的头部添加一个冒号, 冒号之后指定超类: ```KOTLIN open class Base(p: Int) class Derived(p: Int) : Base(p) ``` 如果子类有主构造器, 那么可以(而且必须)在主构造器中使用主构造器的参数来初始化基类. 如果子类没有主构造器, 那么所有的次级构造器都必须使用 `super` 关键字来初始化基类, 或者委托到另一个构造器, 由被委托的构造器来初始化基类. 注意, 这种情况下, 不同的次级构造器可以调用基类中不同的构造器: ```KOTLIN class MyView : View { constructor(ctx: Context) : super(ctx) constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) } ``` ## `open` 关键字 在 Kotlin 中, `open` 关键字表示一个类或一个成员 (函数或属性) 在子类中能够被覆盖. 默认情况下, Kotlin 类及其成员都是 final 的, 也就是说, 除非你明确的将它们标记为 `open`, 否则类不能被继承, 成员不能被覆盖: ```KOTLIN // 带有 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`: ```KOTLIN // 带有 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.") } } ``` ## 方法的覆盖 Kotlin 要求使用明确的修饰符来标识允许被子类覆盖的成员, 也要求使用明确的修饰符来标识对超类成员的覆盖: ```KOTLIN open class Shape { open fun draw() { /*...*/ } fun fill() { /*...*/ } } class Circle() : Shape() { override fun draw() { /*...*/ } } ``` 对于 `Circle.draw()` 必须添加 `override` 修饰符. 如果遗漏了这个修饰符, 编译器将会报告错误. 如果一个函数没有标注 `open` 修饰符, 比如上例中的 `Shape.fill()`, 那么不允许在子类中声明一个同名同参的方法, 无论是否添加 `override` 修饰符, 都不可以. 在一个 final 类(也就是, 没有添加 `open` 修饰符的类)的成员上添加 `open` 修饰符, 不会发生任何效果. 当一个子类成员标记了 `override` 修饰符来覆盖父类成员时, 覆盖后的子类成员本身也将是 open 的, 因此子类成员可以被自己的子类再次覆盖. 如果你希望禁止这种再次覆盖, 可以使用 `final` 关键字: ```KOTLIN open class Rectangle() : Shape() { final override fun draw() { /*...*/ } } ``` ## 属性的覆盖 属性的覆盖方式与方法覆盖相同; 超类中声明的属性在后代类中再次声明时, 必须使用 `override` 关键字来标记, 而且覆盖后的属性数据类型必须与超类中的属性数据类型兼容. 超类中声明的属性, 在后代类中可以使用带初始化器的属性来覆盖, 也可以使用带 `get` 方法的属性来覆盖: ```KOTLIN open class Shape { open val vertexCount: Int = 0 } class Rectangle : Shape() { override val vertexCount = 4 } ``` 你也可以使用一个 `var` 属性覆盖一个 `val` 属性, 但不可以反过来使用一个 `val` 属性覆盖一个 `var` 属性. 允许这种覆盖的原因是, `val` 属性本质上只是定义了一个 `get` 方法, 使用 `var` 属性来覆盖它, 只是向后代类中添加了一个 `set` 方法. 注意, 你可以在主构造器的属性声明中使用 `override` 关键字: ```KOTLIN interface Shape { val vertexCount: Int } class Rectangle(override val vertexCount: Int = 4) : Shape // 长方形总是拥有 4 个顶点 class Polygon : Shape { override var vertexCount: Int = 0 // 多边形的顶点数目不定, 可以变更为任何数字 } ``` ## 子类的初始化顺序 子类新实例构造的过程中, 首先完成的第一步是要初始化基类 (顺序上仅次于计算传递给基类构造器的参数值), 因此要在子类的初始化逻辑之前执行. ```KOTLIN //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` 关键字来调用超类中的函数和属性访问器的实现: ```KOTLIN 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)的超类: ```KOTLIN 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() } ``` ## 覆盖的规则 在 Kotlin 中, 类继承中的方法实现问题, 遵守以下规则: 如果一个类从它的直接超类中继承了同一个成员的多个实现, 那么这个子类必须覆盖这个成员, 并提供一个自己的实现(可以使用继承得到的多个实现中的某一个). 为了表示使用的方法是从哪个超类继承得到的, 可以使用 `super` 关键字, 将超类名称放在尖括号类, 比如, `super`: ```KOTLIN open class Rectangle { open fun draw() { /* ... */ } } interface Polygon { fun draw() { /* ... */ } // 接口的成员默认是 'open' 的 } class Square() : Rectangle(), Polygon { // 编译器要求 draw() 方法必须覆盖: override fun draw() { super.draw() // 调用 Rectangle.draw() super.draw() // 调用 Polygon.draw() } } ``` 同时继承 `Rectangle` 和 `Polygon` 是合法的, 但他们都实现了函数 `draw()` 的继承就发生了问题, 因此你需要在 `Square` 类中覆盖函数 `draw()`, 并提供单独的实现, 这样才能消除歧义.