在 Kotlin 中,override 关键字用于标记一个子类中的方法或属性是从父类继承并在子类中重新实现或覆盖的。这个关键字不仅明确表示了该成员是对父类成员的重写,同时也使得编译器可以进行相关的类型检查,确保父类中确实存在相应的成员供重写。

1. override 关键字的使用场景
1.1 重写父类的方法

在 Kotlin 中,如果你希望在子类中重写父类的某个方法,必须在子类的方法前加上 override 关键字,并且父类的方法必须用 open 关键字标记为可被重写。

open class Animal {
    open fun sound() {
        println("Animal makes a sound")
    }
}

class Dog : Animal() {
    override fun sound() {
        println("Dog barks")
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

在这个例子中:

  • Animal 类中的 sound() 方法被标记为 open,允许子类重写它。
  • Dog 类通过 override 关键字重写了 sound() 方法,并提供了自己的实现。
1.2 重写父类的属性

override 关键字同样可以用于属性。当子类需要提供与父类属性相同的名称但不同的实现时,使用 override 来覆盖父类的属性。父类的属性也必须用 open 标记。

open class Shape {
    open val name: String = "Shape"
}

class Rectangle : Shape() {
    override val name: String = "Rectangle"
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

在这个例子中:

  • Shape 类有一个 open 的属性 name
  • Rectangle 类通过 override 关键字覆盖了 name 属性,并提供了新的实现。
2. 使用 override 时的注意事项
2.1 override 必须与 open 配合使用

Kotlin 中的类和它们的成员(方法、属性)默认是 final 的,这意味着它们不能被继承或重写。如果你想让某个成员可被重写,必须在父类中使用 open 关键字。

open class Parent {
    open fun greet() {
        println("Hello from Parent")
    }
}

class Child : Parent() {
    override fun greet() {
        println("Hello from Child")
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
2.2 子类方法的签名必须与父类一致

在使用 override 重写方法时,子类的方法签名(包括返回类型、参数类型)必须与父类的被重写方法一致。如果签名不一致,编译器会报错。

open class Vehicle {
    open fun drive(speed: Int) {
        println("Driving at $speed km/h")
    }
}

class Car : Vehicle() {
    override fun drive(speed: Int) {
        println("Car is driving at $speed km/h")
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

这里的 drive 方法在子类 Car 中被重写,且签名与父类的 drive 方法一致。

2.3 override 属性的可见性可以被改变

在重写属性时,子类可以修改属性的可见性,但只能使其变得更加开放,而不能更严格。例如,可以将 protected 属性重写为 public,但不能将 public 属性重写为 private

open class Base {
    protected open val number: Int = 10
}

class Derived : Base() {
    public override val number: Int = 20  // 可见性由 protected 改为 public
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
3. overridesuper 关键字

在重写父类的方法时,如果你需要在子类的实现中调用父类的原始方法,可以使用 super 关键字。

open class Parent {
    open fun greet() {
        println("Hello from Parent")
    }
}

class Child : Parent() {
    override fun greet() {
        super.greet()  // 调用父类的 greet 方法
        println("Hello from Child")
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

在这个例子中,Child 类中的 greet 方法首先调用了父类的 greet 方法,然后再执行自己的逻辑。

4. override 与接口

在实现接口时,也可能需要使用 override 关键字。因为接口中的方法默认是 open 的,所以在实现接口时,必须使用 override 来标记实现的方法。

interface Greeter {
    fun greet()
}

class FormalGreeter : Greeter {
    override fun greet() {
        println("Good day!")
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

在这个例子中,FormalGreeter 类实现了 Greeter 接口,并通过 override 关键字提供了 greet 方法的具体实现。

5. override 的实际应用

override 关键字在面向对象编程中非常重要,尤其在设计继承层次结构时。它使得代码更具可读性,明确表示哪些方法或属性在子类中有新的实现,从而避免潜在的错误。

  • 增强功能:通过重写父类的方法,可以在子类中增加额外的行为或功能。
  • 多态性:结合 overrideopen,Kotlin 支持多态性,可以根据运行时对象的类型来决定调用哪个方法。
  • 接口实现:确保类正确实现接口中的所有方法,override 是不可或缺的。
总结
  • override 关键字用于在子类中重写父类或接口中的方法或属性。
  • 父类的成员必须使用 open 关键字标记为可重写,才能在子类中使用 override
  • 子类重写的方法或属性的签名必须与父类一致。
  • override 使得代码的意图更加明确,并帮助编译器进行更严格的类型检查。