ZetCode

Kotlin final 关键字

最后修改于 2025 年 4 月 19 日

Kotlin 的 final 关键字提供了对继承和方法重写的控制。默认情况下,Kotlin 类和成员是 final 的,除非显式标记为 open。本教程深入探讨了 final 关键字。

基本定义

Kotlin 中的 final 关键字可以防止继承或重写。当应用于类时,它不能被继承。当应用于成员时,它不能在子类中被重写。Kotlin 默认将所有内容设为 final,以确保安全。

Final 类

一个 final 类不能被继承。在 Kotlin 中,所有类默认都是 final 的,除非使用 open 关键字标记。这与 Java 不同。

FinalClass.kt
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 标记。这可以防止意外的重写。

FinalMethod.kt
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 标记。

FinalProperty.kt
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 成员没有任何效果。

FinalClassOpenMembers.kt
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,以防止在子类中进一步重写。当你想要允许重写但将其限制在一个级别时,这很有用。

FinalOverride.kt
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 的,不能被继承。这是因为数据类旨在作为简单的值持有者,而继承可能会使它们的行为复杂化。

FinalDataClass.kt
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 的,提供受控的继承。

FinalSealedClass.kt
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 关键字的最佳实践

来源

Kotlin 继承文档

本教程深入探讨了 Kotlin 的 final 关键字,展示了它如何控制继承和重写。我们探讨了 final 类、方法、属性以及数据类和密封类的特殊情况。Kotlin 默认的 final 行为促进了更安全、更易于维护的代码。

作者

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

列出 所有 Kotlin 教程