Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

基类初始化

当一个类继承另一个类时,Kotlin 会保证两个类都得到正确初始化。

Kotlin 通过确保调用构造函数来创建有效的对象:

  • 成员对象的构造函数。
  • 派生类中新增的对象的构造函数。
  • 基类的构造函数。

继承示例中,基类没有构造函数参数。如果基类构造函数参数,派生类在构造过程中必须提供这些参数。

以下是第一个带有构造函数参数的 GreatApe 示例,进行了重写:

// BaseClassInit/GreatApe3.kt
package baseclassinit
import atomictest.eq

open class GreatApe(
  val weight: Double,
  val age: Int
)

open class Bonobo(weight: Double, age: Int) :
  GreatApe(weight, age)

class Chimpanzee(weight: Double, age: Int) :
  GreatApe(weight, age)

class BonoboB(weight: Double, age: Int) :
  Bonobo(weight, age)

fun GreatApe.info() = "wt: $weight age: $age"

fun main() {
  GreatApe(100.0, 12).info() eq
    "wt: 100.0 age: 12"
  Bonobo(110.0, 13).info() eq
    "wt: 110.0 age: 13"
  Chimpanzee(120.0, 14).info() eq
    "wt: 120.0 age: 14"
  BonoboB(130.0, 15).info() eq
    "wt: 130.0 age: 15"
}

在从 GreatApe 继承时,您必须将必要的构造函数参数传递给 GreatApe 基类,否则将会得到编译时错误消息。

在 Kotlin 为对象创建内存之后,它首先调用基类构造函数,然后调用下一个派生类的构造函数,依此类推,直到达到最派生的构造函数。这样,所有构造函数调用都可以依赖于在它们之前创建的所有子对象的有效性。事实上,这是它所知道的唯一内容;Bonobo 知道它从 GreatApe 继承,并且 Bonobo 构造函数可以调用 GreatApe 类中的函数,但 GreatApe 无法知道它是 Bonobo 还是 Chimpanzee,也无法调用那些子类特定的函数。

在从一个类继承时,您必须在基类名称后面提供基类构造函数的参数。这在对象构造过程中调用基类构造函数:

// BaseClassInit/NoArgConstructor.kt
package baseclassinit

open class SuperClass1(val i: Int)
class SubClass1(i: Int) : SuperClass1(i)

open class SuperClass2
class SubClass2 : SuperClass2()

当基类构造函数没有参数时,Kotlin 仍然需要在基类名称后面加上空括号,以便无参数地调用该构造函数。

如果基类中有辅助构造函数,您可以调用其中一个:

// BaseClassInit/House.kt
package baseclassinit
import atomictest.eq

open class House(
  val address: String,
  val state: String,
  val zip: String
) {
  constructor(fullAddress: String) :
    this(fullAddress.substringBefore(", "),
      fullAddress.substringAfter(", ")
        .substringBefore(" "),
      fullAddress.substringAfterLast(" "))
  val fullAddress: String
    get() = "$address, $state $zip"
}

class VacationHouse(
  address: String,
  state: String,
  zip: String,
  val startMonth: String,
  val endMonth: String
) : House(address, state, zip) {
  override fun toString() =
    "Vacation house at $fullAddress " +
    "from $startMonth to $endMonth"
}

class TreeHouse(
  val name: String
) : House("Tree Street, TR 00000") {
  override fun toString() =
    "$name tree house at $fullAddress"
}

fun main() {
  val vacationHouse = VacationHouse(
    address = "8 Target St.",
    state = "KS",
    zip = "66632",
    startMonth = "May",
    endMonth = "September")
  vacationHouse eq
    "Vacation house at 8 Target St., " +
    "KS 66632 from May to September"
  TreeHouse("Oak") eq
    "Oak tree house at Tree Street, TR 00000"
}

VacationHouseHouse 继承时,它将适当的参数传递给主要的 House 构造函数。它还添加了自己的参数 startMonthendMonth - 您不受限于基类中参数的数量、类型或顺序。您唯一的责任是在调用基类构造函数时提供正确的参数。

通过在基类构造函数调用中传递匹配的构造函数参数来调用重载的基类构造函数。您可以在 VacationHouseTreeHouse 的定义中看到这一点。每个类都调用不同的基类构造函数。

在派生类的辅助构造函数内部,您可以调用基类构造函数或不同的派生类构造函数:

// BaseClassInit/OtherConstructors.kt
package baseclassinit
import atomictest.eq

open class Base(val i: Int)

class Derived : Base {
  constructor(i: Int) : super(i)
  constructor() : this(9)
}

fun main() {
  val d1 = Derived(11)
  d1.i eq 11
  val d2 = Derived()
  d2.i eq 9
}

要调用基类构造函数,使用 super 关键字,将构造函数参数传递给它,就像调用函数一样。使用 this 调用同一类的另一个构造函数。

练习和解答可以在 www.AtomicKotlin.com 找到。