ZetCode

Kotlin super 关键字

最后修改于 2025 年 4 月 19 日

Kotlin 的继承系统允许类扩展父类的功能。super 关键字对于访问父类成员至关重要。本教程通过实际例子深入探讨了 super 关键字。

基本定义

Kotlin 中的 super 关键字指的是当前类的父类。它用于访问父类的属性、方法和构造函数。当重写方法或处理继承层次结构时,super 尤其重要。

访问父类属性

当子类具有与父类同名的属性时,super 关键字可以访问父类的属性。这有助于避免继承场景中的命名冲突。

ParentProperty.kt
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 调用父类的实现。这允许扩展父类的功能,同时保持原始行为。

ParentMethod.kt
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 关键字用于从子类构造函数调用父类构造函数。这确保了继承层次结构的正确初始化。

ParentConstructor.kt
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 指定要调用的父接口。这解决了多重继承中的歧义。

MultipleInheritance.kt
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>.foosuper<B>.foo,我们显式地调用每个接口的实现。输出显示了所有三个方法调用。

在嵌套类中访问父类

在嵌套类场景中,super 可以使用限定 this 语法访问外部类的父类。这对于复杂的类层次结构很有用。

NestedClass.kt
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 可以委托给特定的父类构造函数。这提供了对象初始化的灵活性。

ConstructorDelegation.kt
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 可以访问原始属性值。这允许修改父类的行为,同时保留对原始值的访问。

PropertyOverride.kt
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 的最佳实践

来源

Kotlin 继承文档

本教程深入介绍了 Kotlin 的 super 关键字,展示了它在继承场景中的各种用法。我们探讨了属性访问、方法重写、构造函数初始化和多重继承解析。正确使用 super 有助于创建可维护的类层次结构。

作者

我的名字是 Jan Bodnar,我是一位充满激情的程序员,拥有多年的编程经验。自 2007 年以来,我一直在撰写编程文章。到目前为止,我写了 1400 多篇文章和 8 本电子书。 我有超过八年的编程教学经验。

列出 所有 Kotlin 教程