📜  Kotlin 中的覆盖规则

📅  最后修改于: 2022-05-13 01:55:19.992000             🧑  作者: Mango

Kotlin 中的覆盖规则

在任何面向对象的编程语言中,覆盖是允许子类或子类提供其超类或父类之一已经提供的方法的特定实现的功能。您决定您的新类必须重新定义从父类之一继承的方法之一。这被称为覆盖;

如果您已经使用Java编程,您会发现 Kotlin 是一种更明确的语言。在Java中,每个方法默认都是虚拟的;因此,每个方法都可以被任何派生类覆盖。在 Kotlin 中,您必须将函数标记为正在打开才能重新定义它。为此,您需要将 open 关键字作为前缀添加到方法定义中,并且在重新定义方法时,您必须特别使用 override 关键字对其进行标记:

Kotlin
abstract class SingleEngineAirplane protected constructor() {
  abstract fun fly()
}
  
class CesnaAirplane : SingleEngineAirplane() {
   override fun fly() {
     println("Flying a cessna")
   }
}


Kotlin
class CesnaAirplane : SingleEngineAirplane() {
  final override fun fly() {
    println("Flying a cessna")
  }
}


Kotlin
open class Base {
  open val property1: String
  get() = "Base::value"
}
  
class Derived1 : Base() {
  override val property1: String
  get() = "Derived::value"
}
  
class Derived2(override val property1: String) : Base() {}


Kotlin
open class BaseB(open val propertyFoo: String) {}
  
class DerivedB : BaseB("") {
  private var _propFoo: String = ""
  override var propertyFoo: String
  get() = _propFoo
  set(value) {
    _propFoo = value
  }
}
  
fun main(args: Array) {
  val baseB = BaseB("BaseB:value")
  val derivedB= DerivedB()
  derivedB.propertyFoo = "on the spot value"
  println("BaseB:${baseB.propertyFoo}")
  println("DerivedB:${derivedB.propertyFoo}")
}


Kotlin
open class Image {
  open fun save(output: OutputStream) {
    println("Some logic to save an image")
   }
 }
  
interface VendorImage {
  fun save(output: OutputStream) {
    println("Vendor saving an image")
   }
 }
  
class PNGImage : Image(), VendorImage {
  override fun save(output: OutputStream) {
    super.save(output)
    super.save(output)
   }
}
  
fun main(args: Array) {
  val pngImage = PNGImage()
  val os = ByteArrayOutputStream()
  pngImage.save(os)
}


您始终可以通过在方法前面添加 final 关键字来禁止任何派生类覆盖该函数。使用前面的示例,我们不希望任何 Cessna 模型重新定义该方法:

科特林

class CesnaAirplane : SingleEngineAirplane() {
  final override fun fly() {
    println("Flying a cessna")
  }
}

您不仅限于功能。由于 Kotlin 从 C# 中借用了属性的概念,因此您还可以将属性标记为虚拟:

科特林

open class Base {
  open val property1: String
  get() = "Base::value"
}
  
class Derived1 : Base() {
  override val property1: String
  get() = "Derived::value"
}
  
class Derived2(override val property1: String) : Base() {}

如果您的编码逻辑需要,您可以使用 var 覆盖 val 属性,但反过来是不可能的:

科特林

open class BaseB(open val propertyFoo: String) {}
  
class DerivedB : BaseB("") {
  private var _propFoo: String = ""
  override var propertyFoo: String
  get() = _propFoo
  set(value) {
    _propFoo = value
  }
}
  
fun main(args: Array) {
  val baseB = BaseB("BaseB:value")
  val derivedB= DerivedB()
  derivedB.propertyFoo = "on the spot value"
  println("BaseB:${baseB.propertyFoo}")
  println("DerivedB:${derivedB.propertyFoo}")
}

在某些情况下,您需要从一个类和至少一个接口派生,并且都定义和实现具有相同名称和参数的方法。在这种情况下,继承规则会强制您重写该方法。如果您创建对象的新实例并调用直接父类通用的方法,编译器应该链接到哪个方法。因此,您需要消除歧义并提供实现;它可以使用任何或两个父类的实现。想象一下,您有一个用于处理不同图像格式的类层次结构,并且您希望将它们与第三方层次结构统一起来。

由于两个类层次结构都带有保存函数的定义,因此您需要覆盖它们:

科特林

open class Image {
  open fun save(output: OutputStream) {
    println("Some logic to save an image")
   }
 }
  
interface VendorImage {
  fun save(output: OutputStream) {
    println("Vendor saving an image")
   }
 }
  
class PNGImage : Image(), VendorImage {
  override fun save(output: OutputStream) {
    super.save(output)
    super.save(output)
   }
}
  
fun main(args: Array) {
  val pngImage = PNGImage()
  val os = ByteArrayOutputStream()
  pngImage.save(os)
}

如果 VendorImage 接口没有提供实现,则不会强制执行覆盖。引用父实现是通过 super 完成的,正如您在前面的实现中可能已经注意到的那样。