函数重载
本教程共 93 篇 · 第 33 篇 · 更新于 2026-08-08 · 约 8 分钟阅读
本节目标:理解函数重载——同名函数靠不同的参数个数、参数类型或参数标签共存,并记住“只靠返回类型不同不能构成重载”。
有时候你会发现,几个函数干的是同一类事,只是细节不同。比如“计算乘积”,两个数相乘、三个数相乘、字符串拼也是“相加”。Swift 允许你给它们起同一个名字,靠参数的差异来区分,这叫“函数重载”(overloading)。调用时编译器按你传的实参自动挑对那一个,名字统一、语义清晰。
1-1 靠参数个数重载
最直观的重载:同名函数,参数数量不同。
func product(num1: Int, num2: Int) {
print("乘积 =", num1 * num2)
}
func product(num1: Int, num2: Int, num3: Int) {
print("乘积 =", num1 * num2 * num3)
}
product(num1: 23, num2: 34)
product(num1: 23, num2: 34, num3: 55)
两个都叫 product,但一个吃两个参数、一个吃三个。你传两个它就调两个的版本,传三个就调三个的。调用处名字一致,不用记 product2、product3 这种别扭的变体。
Note编译器选择重载版本的依据,是“调用时实参的个数和类型 + 标签”。它会在编译期就定下来调哪个,不是运行时猜。所以重载不会带来运行时的性能损耗。
1-2 靠参数类型重载
同名、同数量,但参数类型不同,也能重载。典型例子:整数相加和字符串拼接,都叫 addition。
func addition(num1: Int, num2: Int) {
print("结果 =", num1 + num2)
}
func addition(num1: String, num2: String) {
print("结果 =", num1 + num2)
}
addition(num1: 23, num2: 34)
addition(num1: "你好,", num2: "世界")
两个 addition 都接两个参数,但一个是 Int、一个是 String。编译器看实参类型决定调哪个:传整数就走整数版,传字符串就走字符串版。同一个名字,自动适配不同类型,调用者体验很统一。
1-3 靠参数标签重载
同名、同数量、同类型,只要参数标签不同,也算不同的函数,可以重载。
func area(length: Int, width: Int) {
print("面积 =", length * width)
}
func area(l: Int, w: Int) {
print("面积 =", l * w)
}
area(length: 23, width: 3)
area(l: 13, w: 3)
两个函数都接收两个 Int,但标签分别是 length:width: 和 l:w:。因为标签是函数身份的一部分,它们被视为不同函数。调用时写 length: 就进第一个,写 l: 就进第二个。这再次印证了前面讲的“函数靠名字加标签整体区分”。
1-4 不能只靠返回类型重载
这是重载最容易踩的坑:只有返回类型不同,不足以构成重载,编译器会报错。
// 下面这两个不能共存,编译报错
// func getValue() -> Int { return 1 }
// func getValue() -> String { return "一" }
原因在于,如果你写 getValue() 却没把结果赋给任何明确类型的变量,编译器无从知道你想要 Int 版还是 String 版。所以 Swift 规定:重载必须体现在参数上(个数、类型、标签),不能只体现在返回值上。
Warning记住一条铁律:返回类型不参与重载区分。想让两个同名函数共存,一定让它们的参数签名有差异。如果你发现“只差返回类型”,那说明它们其实该起不同的名字,而不是重载。
1-5 重载的好处与节制
重载的优点很明显:减少函数名的数量,让一组相关的操作共用一个名字,调用处读起来像在用“同一个能力”的不同形态。比如标准库里 print 既能打印也能定制分隔符,min、max 既能比两个数也能比一堆数,背后都是重载在撑。
但也要有节制:
第一,别为了重载而重载。如果两个函数语义差别很大,硬起同名反而让人困惑,不如起不同的名字。
第二,重载版本之间行为要一致。同名函数调用者会默认“它们干的是一类事”,如果一个版本偷偷做了完全不同的事,就是坑人。
第三,标签重载尤其要小心,确保不同标签对应的语义确实等价,别让 area(length:width:) 和 area(l:w:) 算出不一样的东西。
Tip判断要不要重载,问自己一句:这几个函数是不是“同一件事的不同输入形态”?是,就共用名字;不是,就分开起名。模糊地带宁可分开,清晰优先于省名字。
1-6 重载遇到默认参数时要小心
重载和默认参数碰在一起,有时会产生“歧义”。比如你有一个 greet(_ name: String),又重载一个 greet(_ name: String, formally: Bool = false)——调用 greet("小明") 时,编译器会选参数更少的那个,因为更匹配。但如果你故意写出多个“都能匹配同一调用”的重载,编译器可能不知道选谁,直接报错。
所以经验是:重载版本之间要有清晰的“形状差异”,别让两个版本都能无缝接住同一组实参。当默认参数已经能覆盖“少传一个”的场景时,就没必要再为它单独重载一个函数。重载解决“形状不同”,默认参数解决“同形状少传”,各管一摊,别越界。
1-7 重载解析的小结
编译器挑重载版本时,遵循“最具体匹配优先”的原则:参数个数、类型、标签越贴合你传的实参,就越优先。如果出现多个版本同样贴切、又都不明显更优,编译器会报歧义错误,而不是瞎猜。
这对你是好消息:歧义在编译期就暴露,不会埋到运行时。写重载时,只要保证各版本“形状”有清晰差异,让编译器总能在编译期唯一确定调谁,重载就是安全又优雅的工具。
重载虽好,但不是越多越好。一组重载超过四五个,调用者反而要猜“我该调哪个”。如果发现重载越加越多,可能说明这些操作本就不该共用一个名字,拆开反而清晰。
记住重载的铁律就够了:同名可以,但签名必须不同;只差返回类型不行。其余的交给编译器去分辨,它会在编译期帮你确定该调哪一个。
函数重载是 Swift 多态最轻量、最常用的入口。到这里,流程控制与函数两大模块就讲完了。函数让你把代码打包复用,重载让同名的函数灵活适配不同输入——它们一起构成了你写 Swift 的基本功。后面我们会进入闭包、可选类型与错误处理等更深入的主题。