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 中,重载的构造函数被称为辅助构造函数。构造函数参数列表(紧跟在类名之后的部分)与属性的初始化和 init 块组合在一起,称为主构造函数

要创建一个辅助构造函数,请使用 constructor 关键字,后面跟着一个与所有其他主要和辅助参数列表都不同的参数列表。在辅助构造函数内部,this 关键字调用主构造函数或另一个辅助构造函数:

// SecondaryConstructors/WithSecondary.kt
package secondaryconstructors
import atomictest.*

class WithSecondary(i: Int) {
  init {
    trace("Primary: $i")
  }
  constructor(c: Char) : this(c - 'A') {
    trace("Secondary: '$c'")
  }
  constructor(s: String) :
    this(s.first()) {             // [1]
    trace("Secondary: \"$s\"")
  }
  /* 不调用主构造函数将不会通过编译:
  constructor(f: Float) {         // [2]
    trace("Secondary: $f")
  }
  */
}

fun main() {
  fun sep() = trace("-".repeat(10))
  WithSecondary(1)
  sep()
  WithSecondary('D')
  sep()
  WithSecondary("Last Constructor")
  trace eq """
    Primary: 1
    ----------
    Primary: 3
    Secondary: 'D'
    ----------
    Primary: 11
    Secondary: 'L'
    Secondary: "Last Constructor"
  """
}

从辅助构造函数调用另一个构造函数(使用 this)必须发生在额外的构造函数逻辑之前,因为构造函数体可能依赖于这些其他初始化。因此,它位于构造函数体之前。

参数列表决定要调用的构造函数。WithSecondary(1) 匹配主构造函数,WithSecondary('D') 匹配第一个辅助构造函数,WithSecondary("Last Constructor") 匹配第二个辅助构造函数。[1] 中的 this() 调用匹配第一个辅助构造函数,并且可以在输出中看到调用链。

主构造函数必须始终被调用,可以直接调用,也可以通过调用辅助构造函数来调用。否则,Kotlin 会在编译时生成错误,就像 [2] 中一样。因此,所有可以在构造函数之间共享的通用初始化逻辑都应该放在主构造函数中。

在使用辅助构造函数时,init 部分是不必要的:

// SecondaryConstructors/GardenItem.kt
package secondaryconstructors
import atomictest.eq
import secondaryconstructors.Material.*

enum class Material {
  Ceramic, Metal, Plastic
}

class GardenItem(val name: String) {
  var material: Material = Plastic
  constructor(
    name: String, material: Material    // [1]
  ) : this(name) {                      // [2]
    this.material = material            // [3]
  }
  constructor(
    material: Material
  ) : this("Strange Thing", material)   // [4]
  override fun toString() = "$material $name"
}

fun main() {
  GardenItem("Elf").material eq Plastic
  GardenItem("Snowman").name eq "Snowman"
  GardenItem("Gazing Ball", Metal) eq   // [5]
    "Metal Gazing Ball"
  GardenItem(material = Ceramic) eq
    "Ceramic Strange Thing"
}
  • [1] 只有主构造函数的参数可以通过 valvar 声明为属性。
  • [2] 你不能为辅助构造函数声明返回类型。
  • [3] material 参数与属性名称相同,因此我们使用 this 进行了消除歧义。
  • [4] 辅助构造函数体是可选的(尽管您仍然必须包含一个显式的 this() 调用)。

在调用 [5] 中的第一个辅助构造函数时,属性 material 被赋值了两次。首先,在调用主构造函数(在 [2] 中)和初始化所有类属性时,将 Plastic 值赋给了它,然后在 [3] 处将其更改为 material 参数。

GardenItem 类可以使用默认参数来简化,用单个主构造函数替换辅助构造函数。

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