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

重载

在没有默认参数支持的语言中,通常使用重载来模拟该功能。

术语重载是指函数的名称:您可以使用相同的名称(为该名称重载)来表示不同的函数,只要参数列表不同即可。以下是对成员函数 f() 进行重载的示例:

// Overloading/Overloading.kt
package overloading
import atomictest.eq

class Overloading {
  fun f() = 0
  fun f(n: Int) = n + 2
}

fun main() {
  val o = Overloading()
  o.f() eq 0
  o.f(11) eq 13
}

Overloading 类中,我们看到两个具有相同名称 f() 的函数。函数的签名由名称、参数列表和返回类型组成。Kotlin 通过比较签名来区分不同的函数。在重载函数时,参数列表必须是唯一的,不能根据返回类型进行重载。

上述调用表明它们确实是不同的函数。函数签名还包括关于封闭类的信息(或者如果它是扩展函数,则是接收者类型)。

请注意,如果类已经有一个与扩展函数具有相同签名的成员函数,则 Kotlin 会优先选择成员函数。但是,您可以使用扩展函数重载成员函数:

// Overloading/MemberVsExtension.kt
package overloading
import atomictest.eq

class My {
  fun foo() = 0
}

fun My.foo() = 1             // [1]

fun My.foo(i: Int) = i + 2   // [2]

fun main() {
  My().foo() eq 0
  My().foo(1) eq 3
}
  • [1] 声明重复成员函数的扩展是没有意义的,因为它永远不会被调用。
  • [2] 您可以使用扩展函数通过提供不同的参数列表来重载成员函数。

不要使用重载来模拟默认参数。也就是说,不要这样做:

// Overloading/WithoutDefaultArguments.kt
package withoutdefaultarguments
import atomictest.eq

fun f(n: Int) = n + 373
fun f() = f(0)

fun main() {
  f() eq 373
}

没有参数的函数只是调用了第一个函数。通过使用默认参数,这两个函数可以被一个函数替代:

// Overloading/WithDefaultArguments.kt
package withdefaultarguments
import atomictest.eq

fun f(n: Int = 0) = n + 373

fun main() {
  f() eq 373
}

在这两个示例中,您可以在没有参数的情况下调用函数,或者通过传递整数值来调用函数。更推荐使用 WithDefaultArguments.kt 中的形式。

在使用重载函数和默认参数时,调用重载函数会搜索“最近”的匹配项。在下面的示例中,`

main()中的foo()` 调用不会调用带有默认参数 99 的函数的第一个版本,而是调用第二个版本,即没有参数的版本:

// Overloading/OverloadedVsDefaultArg.kt
package overloadingvsdefaultargs
import atomictest.*

fun foo(n: Int = 99) = trace("foo-1-$n")

fun foo() {
  trace("foo-2")
  foo(14)
}

fun main() {
  foo()
  trace eq """
    foo-2
    foo-1-14
  """
}

您永远无法使用默认参数 99,因为 foo() 总是调用 f() 的第二个版本。

为什么重载有用?它允许您更清楚地表达“同一主题的变化”,而不是被迫使用不同的函数名称。假设您想要添加函数:

// Overloading/OverloadingAdd.kt
package overloading
import atomictest.eq

fun addInt(i: Int, j: Int) = i + j
fun addDouble(i: Double, j: Double) = i + j

fun add(i: Int, j: Int) = i + j
fun add(i: Double, j: Double) = i + j

fun main() {
  addInt(5, 6) eq add(5, 6)
  addDouble(56.23, 44.77) eq
    add(56.23, 44.77)
}

addInt() 接受两个 Int,返回一个 Int,而 addDouble() 接受两个 Double,返回一个 Double。如果没有重载,您无法将操作命名为 add(),因此程序员通常将 whathow 混为一谈,以生成唯一的名称(您也可以使用随机字符创建唯一的名称,但是通常的模式是使用参数类型等有意义的信息)。相比之下,重载的 add() 更清晰。

在语言中缺少重载并不是一个严重的困扰,但这个特性提供了宝贵的简化,产生了更可读的代码。使用重载时,您只需要说出 what,这提高了抽象级别,减轻了读者的思维负担。如果您想了解 how,可以查看参数。请注意,重载减少了冗余:如果我们必须说 addInt()addDouble(),那么实际上在函数名称中重复了参数信息。

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