Kotlin open 关键字
最后修改于 2025 年 4 月 19 日
Kotlin 的继承模型在设计时就考虑了明确性。 open 关键字是启用继承和方法重写的关键。本教程通过实际例子深入探讨了 open 关键字。
基本定义
Kotlin 中的 open 关键字将类、方法或属性标记为可继承或可重写。 默认情况下,Kotlin 类和成员是 final 的。 open 修饰符消除了这种限制,允许继承。
基本类继承
要在 Kotlin 中从一个类继承,父类必须用 open 关键字标记。 此示例显示了最简单的继承形式。
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。
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 修饰符。
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,以防止子类进一步重写它。 当您想锁定特定行为时,这很有用。
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 类中的具体实现仍然需要它。
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 修饰符允许子类中的灵活实现。
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 修饰符影响整个类。
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 关键字的最佳实践
- 有意识地使用: 仅当您明确希望继承或重写类和成员时,才将它们标记为
open。 - 记录行为: 清楚地记录应如何重写 open 成员以保持预期的行为。
- 考虑使用 final: 当您希望防止子类进一步重写时,在重写中使用
final。 - 优先使用组合: 有时组合比继承更好,即使类是 open 的。
- 彻底测试: 应充分测试可重写行为,以确保子类不会破坏约定。
来源
本教程深入介绍了 Kotlin 的 open 关键字,展示了它如何启用继承和重写。 我们探讨了类继承、方法和属性重写以及各种相关概念。 适当使用 open 可以使您的 Kotlin 代码更灵活,同时保持对可扩展性的控制。
作者
列出 所有 Kotlin 教程。