ZetCode

Kotlin open 关键字

最后修改于 2025 年 4 月 19 日

Kotlin 的继承模型在设计时就考虑了明确性。 open 关键字是启用继承和方法重写的关键。本教程通过实际例子深入探讨了 open 关键字。

基本定义

Kotlin 中的 open 关键字将类、方法或属性标记为可继承或可重写。 默认情况下,Kotlin 类和成员是 final 的。 open 修饰符消除了这种限制,允许继承。

基本类继承

要在 Kotlin 中从一个类继承,父类必须用 open 关键字标记。 此示例显示了最简单的继承形式。

BasicInheritance.kt
package com.zetcode

open class Animal {
    fun eat() = println("Eating...")
}

class Dog : Animal() {
    fun bark() = println("Woof!")
}

fun main() {
    val dog = Dog()
    dog.eat()  // Output: Eating...
    dog.bark() // Output: Woof!
}

在这里,我们定义了一个 open Animal 类,Dog 从该类继承。 Dog 类可以访问 Animal 的方法,同时添加自己的方法。 如果没有 open,Animal 就不能被继承。

重写方法

要在 Kotlin 中重写一个方法,父方法和子方法都需要修饰符。 父方法必须是 open,子方法必须使用 override

MethodOverride.kt
package com.zetcode

open class Animal {
    open fun makeSound() = println("Animal sound")
}

class Cat : Animal() {
    override fun makeSound() = println("Meow!")
}

fun main() {
    val cat = Cat()
    cat.makeSound() // Output: Meow!
}

Animal 类声明了一个 open makeSound 方法,Cat 重写了该方法。 override 更改了 Cat 实例的行为。 如果父方法没有 open,则无法重写。

重写属性

与方法类似,当父类中标记为 open 时,子类中的属性可以被重写。 子属性必须使用 override 修饰符。

PropertyOverride.kt
package com.zetcode

open class Vehicle {
    open val wheels: Int = 4
}

class Bicycle : Vehicle() {
    override val wheels: Int = 2
}

fun main() {
    val bike = Bicycle()
    println(bike.wheels) // Output: 2
}

Vehicle 类定义了一个 open wheels 属性,默认值为 4。 Bicycle 类使用值 2 重写此属性。 override 更改了 Bicycle 实例的属性值。

防止进一步重写

您可以将重写标记为 final,以防止子类进一步重写它。 当您想锁定特定行为时,这很有用。

FinalOverride.kt
package com.zetcode

open class Shape {
    open fun draw() = println("Drawing shape")
}

open class Circle : Shape() {
    final override fun draw() = println("Drawing circle")
}

class SpecialCircle : Circle() {
    // Cannot override draw() here
}

fun main() {
    val circle = Circle()
    circle.draw() // Output: Drawing circle
}

Circle 类将其 draw 方法设为 final,防止 SpecialCircle 重写它。 这种技术有助于维护类层次结构中的关键行为。

抽象类和 open

抽象类是隐式 open 的,因此它们的成员不需要 open 修饰符即可被重写。 但是,open 类中的具体实现仍然需要它。

AbstractClass.kt
package com.zetcode

abstract class Animal {
    abstract fun makeSound()
    open fun eat() = println("Eating food")
}

class Dog : Animal() {
    override fun makeSound() = println("Woof!")
    override fun eat() = println("Eating dog food")
}

fun main() {
    val dog = Dog()
    dog.makeSound() // Output: Woof!
    dog.eat()      // Output: Eating dog food
}

抽象的 makeSound 方法不需要 open,但具体的 eat 方法需要。 Dog 重写了这两种方法,为每种方法提供了实现。

带有自定义 Getter/Setter 的 Open 属性

当使用自定义访问器重写属性时,open 修饰符允许子类中的灵活实现。

OpenProperty.kt
package com.zetcode

open class Person {
    open val name: String
        get() = "Unknown"
}

class Student : Person() {
    override val name: String
        get() = "Student Name"
}

fun main() {
    val person: Person = Student()
    println(person.name) // Output: Student Name
}

Person 类定义了一个 open name 属性,该属性具有自定义 getter。 Student 使用其自己的实现重写此属性。 实际的运行时类型决定调用哪个 getter。

具有次构造函数的 Open 类

Open 类可以有次构造函数,子类可以使用 super 关键字调用它们。 open 修饰符影响整个类。

SecondaryConstructor.kt
package com.zetcode

open class Vehicle(val wheels: Int) {
    constructor() : this(4)
}

class Motorcycle : Vehicle {
    constructor() : super(2)
}

fun main() {
    val bike = Motorcycle()
    println(bike.wheels) // Output: 2
}

Vehicle 被标记为 open,并且同时具有主构造函数和次构造函数。 Motorcycle 从 Vehicle 继承,并通过其自己的次构造函数调用主构造函数。

open 关键字的最佳实践

来源

Kotlin 继承文档

本教程深入介绍了 Kotlin 的 open 关键字,展示了它如何启用继承和重写。 我们探讨了类继承、方法和属性重写以及各种相关概念。 适当使用 open 可以使您的 Kotlin 代码更灵活,同时保持对可扩展性的控制。

作者

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

列出 所有 Kotlin 教程