Kotlin final 关键字
最后修改于 2025 年 4 月 19 日
Kotlin 的 final 关键字提供了对继承和方法重写的控制。默认情况下,Kotlin 类和成员是 final 的,除非显式标记为 open。本教程深入探讨了 final 关键字。
基本定义
Kotlin 中的 final 关键字可以防止继承或重写。当应用于类时,它不能被继承。当应用于成员时,它不能在子类中被重写。Kotlin 默认将所有内容设为 final,以确保安全。
Final 类
一个 final 类不能被继承。在 Kotlin 中,所有类默认都是 final 的,除非使用 open 关键字标记。这与 Java 不同。
package com.zetcode
final class FinalClass {
fun show() = println("Final class method")
}
// This would cause a compile error
// class SubClass : FinalClass()
在这里,我们显式声明一个 final 类(尽管它在 Kotlin 中是多余的)。任何尝试从这个类继承的行为都将失败。Kotlin 默认的 final 行为通过防止意外继承来促进更好的设计。
Final 方法
一个 final 方法不能在子类中被重写。在 Kotlin 中,所有方法默认都是 final 的,除非使用 open 标记。这可以防止意外的重写。
package com.zetcode
open class Parent {
final fun cannotOverride() = println("This is final")
open fun canOverride() = println("This can be overridden")
}
class Child : Parent() {
override fun canOverride() = println("Overridden method")
// Cannot override cannotOverride()
}
cannotOverride 方法被标记为 final,并且不能在 Child 类中被重写。canOverride 方法是 open 的,可以被重写。这演示了 Kotlin 在继承方面的显式设计。
Final 属性
Final 属性的工作方式与方法类似——它们不能在子类中被重写。Kotlin 属性默认是 final 的,除非使用 open 标记。
package com.zetcode
open class Vehicle {
final val wheels = 4
open val maxSpeed = 100
}
class Car : Vehicle() {
override val maxSpeed = 200
// Cannot override wheels
}
wheels 属性是 final 的,不能被重写,而 maxSpeed 是 open 的,可以在 Car 子类中被重写。这展示了如何在 Kotlin 中控制属性继承。
带有 Open 成员的 Final 类
即使一个类是 final 的(默认),你仍然可以将其成员标记为 open。但是,由于类本身不能被继承,open 成员没有任何效果。
package com.zetcode
class FinalClassWithOpenMembers {
open fun canOverride() = println("Open but in final class")
// This method can't actually be overridden
}
// This would cause a compile error
// class SubClass : FinalClassWithOpenMembers()
在这里,我们看到在一个 final 类中将方法标记为 open 是允许的,但没有意义,因为该类不能被继承。编译器将警告这种冗余使用 open。
Final 重写
你可以将重写标记为 final,以防止在子类中进一步重写。当你想要允许重写但将其限制在一个级别时,这很有用。
package com.zetcode
open class Parent {
open fun canOverride() = println("Parent method")
}
open class Child : Parent() {
final override fun canOverride() = println("Child method")
}
class GrandChild : Child() {
// Cannot override canOverride() here
}
Child 类重写了该方法,但将其标记为 final,防止在 GrandChild 中进一步重写。这种技术提供了受控继承,你可以在其中停止特定级别的重写链。
数据类中的 Final
Kotlin 中的数据类是隐式 final 的,不能被继承。这是因为数据类旨在作为简单的值持有者,而继承可能会使它们的行为复杂化。
package com.zetcode
data class Person(val name: String, val age: Int)
// This would cause a compile error
// class Employee : Person("John", 30)
Person 数据类是自动 final 的,任何尝试从中继承的行为都将失败。这种设计确保数据类在其行为中保持简单和可预测。
密封类中的 Final
密封类是隐式抽象的,只能在同一文件中被继承。它们的子类可以是 final 或 open 的,提供受控的继承。
package com.zetcode
sealed class Result {
final class Success(val data: String) : Result()
open class Error(val message: String) : Result()
}
// Can only extend Result in same file
class NetworkError : Result.Error("Network failure")
在这里,我们看到了一个密封类,它同时具有 final 和 open 的子类。Success 类是 final 的,不能被进一步扩展,而 Error 是 open 的,可以被扩展(但只能在与密封类相同的文件中)。
final 关键字的最佳实践
- 默认为 final: Kotlin 默认的 final 行为很好 - 仅在必要时将类/成员设为 open。
- 记录原因:使用 open 时,记录为什么需要继承,以帮助维护人员。
- 优先使用组合:通常组合比继承更好 - 在将类设为 open 之前考虑这一点。
- 使用 final override:重写时,考虑是否应该使用 final override 允许进一步的重写。
- 了解密封类:对于受控的继承层次结构,请考虑使用密封类而不是 open 类。
来源
本教程深入探讨了 Kotlin 的 final 关键字,展示了它如何控制继承和重写。我们探讨了 final 类、方法、属性以及数据类和密封类的特殊情况。Kotlin 默认的 final 行为促进了更安全、更易于维护的代码。
作者
列出 所有 Kotlin 教程。