Kotlin super 关键字
最后修改于 2025 年 4 月 19 日
Kotlin 的继承系统允许类扩展父类的功能。super 关键字对于访问父类成员至关重要。本教程通过实际例子深入探讨了 super 关键字。
基本定义
Kotlin 中的 super 关键字指的是当前类的父类。它用于访问父类的属性、方法和构造函数。当重写方法或处理继承层次结构时,super 尤其重要。
访问父类属性
当子类具有与父类同名的属性时,super 关键字可以访问父类的属性。这有助于避免继承场景中的命名冲突。
package com.zetcode
open class Vehicle {
open val speed = 100
}
class Car : Vehicle() {
override val speed = 120
fun showSpeed() {
println("Car speed: $speed")
println("Parent speed: ${super.speed}")
}
}
fun main() {
val car = Car()
car.showSpeed()
}
在这里,我们定义了一个具有 speed 属性的 Vehicle 类。Car 类重写了这个属性。使用 super.speed,我们访问父类的 speed 值。输出显示了子类和父类的属性值。
调用父类方法
当重写方法时,您可以使用 super 调用父类的实现。这允许扩展父类的功能,同时保持原始行为。
package com.zetcode
open class Animal {
open fun makeSound() {
println("Animal makes a sound")
}
}
class Dog : Animal() {
override fun makeSound() {
super.makeSound()
println("Dog barks: Woof!")
}
}
fun main() {
val dog = Dog()
dog.makeSound()
}
Dog 类重写了 makeSound 方法,但首先使用 super.makeSound 调用父类的实现。这演示了如何扩展而不是替换父类功能。
父类构造函数初始化
super 关键字用于从子类构造函数调用父类构造函数。这确保了继承层次结构的正确初始化。
package com.zetcode
open class Person(val name: String) {
init {
println("Person initialized: $name")
}
}
class Employee(name: String, val position: String) : Person(name) {
init {
println("Employee initialized: $name, $position")
}
}
fun main() {
val emp = Employee("John Doe", "Developer")
}
在这里,Employee 类使用 : Person(name) 调用 Person 构造函数。此语法隐式地使用 super 来初始化父类。输出显示了从父类到子类的初始化顺序。
使用接口的多重继承
当使用具有冲突方法名称的多个接口时,带有尖括号的 super 指定要调用的父接口。这解决了多重继承中的歧义。
package com.zetcode
interface A {
fun foo() {
println("A's foo")
}
}
interface B {
fun foo() {
println("B's foo")
}
}
class C : A, B {
override fun foo() {
super<A>.foo()
super<B>.foo()
println("C's foo")
}
}
fun main() {
val c = C()
c.foo()
}
C 类实现了接口 A 和 B,它们具有冲突的 foo 方法。使用 super<A>.foo 和 super<B>.foo,我们显式地调用每个接口的实现。输出显示了所有三个方法调用。
在嵌套类中访问父类
在嵌套类场景中,super 可以使用限定 this 语法访问外部类的父类。这对于复杂的类层次结构很有用。
package com.zetcode
open class Outer {
open val value = 10
inner class Inner {
fun showValues() {
println("Inner value: ${this@Outer.value}")
println("Parent value: ${super@Outer.value}")
}
}
}
class Subclass : Outer() {
override val value = 20
}
fun main() {
val outer = Subclass()
val inner = outer.Inner()
inner.showValues()
}
Inner 类可以使用限定的 super 访问 Subclass 中重写的值和 Outer 中的原始值。输出演示了如何在复杂的继承链中导航。
使用 super 的构造函数委托
在次级构造函数中,super 可以委托给特定的父类构造函数。这提供了对象初始化的灵活性。
package com.zetcode
open class Parent {
constructor(message: String) {
println("Parent primary: $message")
}
constructor(num: Int) {
println("Parent secondary: $num")
}
}
class Child : Parent {
constructor(message: String) : super(message) {
println("Child primary: $message")
}
constructor(num: Int) : super(num) {
println("Child secondary: $num")
}
}
fun main() {
val c1 = Child("Hello")
val c2 = Child(42)
}
Child 类有两个构造函数,它们使用 super 委托给不同的 Parent 构造函数。每个初始化路径都会产生不同的输出,展示了构造函数委托的实际应用。
使用 super 重写属性
当使用自定义 getter 重写属性时,super 可以访问原始属性值。这允许修改父类的行为,同时保留对原始值的访问。
package com.zetcode
open class Shape {
open val area: Double = 0.0
}
class Circle(val radius: Double) : Shape() {
override val area: Double
get() = super.area + (Math.PI * radius * radius)
}
fun main() {
val circle = Circle(5.0)
println("Circle area: ${circle.area}")
}
Circle 类重写了 area 属性,但在其计算中使用 super.area 引用父类的值。这演示了如何基于父类属性构建而不是完全替换它们。
使用 super 的最佳实践
- 谨慎使用: 仅当您需要显式引用父类成员时才使用
super。 - 构造函数链: 通过调用适当的父类构造函数来确保正确的构造函数初始化。
- 接口冲突: 使用
super<Type>语法解决多重继承中的方法冲突。 - 文档重写: 清楚地记录您何时以及为什么调用父类实现。
- 避免深层层次结构: 具有频繁 super 调用的深层继承链会使代码更难以维护。
来源
本教程深入介绍了 Kotlin 的 super 关键字,展示了它在继承场景中的各种用法。我们探讨了属性访问、方法重写、构造函数初始化和多重继承解析。正确使用 super 有助于创建可维护的类层次结构。
作者
列出 所有 Kotlin 教程。