Swift 隐藏宝藏:“逆天改命”调整方法重载(function overloading)优先级

本文主要是介绍Swift 隐藏宝藏:“逆天改命”调整方法重载(function overloading)优先级,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

概览

在 Swift 语言中有很多隐藏“宝藏”悄悄深埋在不为人知的角落,静静等待着有缘秃头码农们的大力挖掘。

在这里插入图片描述

而在这里,我们将介绍 Swift 语言中一个非常有用的秘技:方法重载优先级判断以及如何改变它。

在本篇博文中,您将学到如下内容:

  • 概览
  • 1. 什么是方法重载(function overloading)?
  • 2. Swift 中方法重载优先级判断原则
  • 3. 改变游戏规则!
  • 总结

相信学完本课后,小伙伴们一定会对 Swift 语言的博大精深所折服,并将本课学到的这一隐秘技能发扬光大!

那还等什么呢?让我们马上开始宝藏的深入挖掘之旅吧!

Let‘s Go!!!😉


1. 什么是方法重载(function overloading)?

方法重载(function overloading)是很多开发语言都支持的一种特性,它被用来多样化同名方法或函数的调用。使用方法重载可以大大增加同名方法或函数调用的灵活性。

比如,在以下代码中三个 test 函数互为重载:

func test() {print(#function)
}func test(value: Int) {print(#function)
}func test(value: Int, needChanged: Bool) {print(#function)
}

方法重载的一般要求为方法名相同,但方法签名会有所不同。值得说明的是,只考虑签名中的返回类型在某些情况下会产生模棱两可的语义。

func test() {print(#function)
}func test() -> String {print(#function)return "test"
}/* 直接调用 test() 会产生歧义!
error: Test.playground:13:1: error: ambiguous use of 'test()'
*/
test()

如上代码所示,如果只有方法返回类型不同则会让编译器“不知所措”,我们必须显式告诉编译器我们的“精确”意图:

// 会调用返回 String 版本的 test() 方法
let text: String = test()

注意,不要将方法重载和方法重写(overriding)相混淆:后者被用来支持对象中相同方法的多态性;前者在编译时被考虑,后者则在运行时被考虑。

2. Swift 中方法重载优先级判断原则

上面我们了解了 Swift 方法重载的不同形式。那么,对于名称相同的方法来说,如果在调用时满足其中多个重载语义,那么到底实际会使用哪个重载方法呢?

func test() {print(#function)
}func test(value: Int = 11) {print(#function)
}// 实际会调用上面哪个重载方法呢?
test()

如上例所示,我们创建了名为 test 的两个重载方法,如果我们进行 test() 调用,到底会执行上面哪个方法呢?

前 Swift 核心组成员 Jordan Rose 曾在不经意间透露过解决这种情况的“秘诀”:

The general principle is that the most specific overload should win.

那就是:具体胜过一般原则!

这意味着,当方法的多个重载同时满足调用语义时,越具体的重载越会被优先考虑;约束条件越少的重载越会被优先考虑。

func test() {}
func test(value: Int = 11) {}

还拿上面的例子来说,对于 test() 语义来说,第一个方法会被调用,因为它不需要假设用户选择了默认参数,约束条件更少。

类似的,对于下面的例子,由于不需要假设用户在调用时选择单个参数 test 方法的不定参数重载版本,所以实际 test() 采用的是 test(_ value: Int) 这一约束更少、更简单的重载调用:

func test(_ value: Int) {print(#function)
}func test(_ values: Int...) {print("不定参数(Variadic):\(#function)")
}// 会调用第一个方法重载
test(11)

同理,如果非要在普通重载和泛型重载中选择一个的话,编译器将会毫不犹豫的选择前者:

func test(_ value: Int) {print(#function)
}func test<N: Numeric>(_ value: N) {print("泛型:\(#function)")
}// 将会调用非泛型版本的重载
test(11)

俗话说得好:“我命由我不由天”!小伙伴们的疑问是:我们能不能改变编译器对于多个重载版本的选择偏好呢?

答案是肯定的!

3. 改变游戏规则!

在某些情况下,我们希望让本该“不受人待见”的重载方法重新“翻身做主人”。比如,对于之前泛型重载的那个例子,如果我们希望泛型版本的重载优先被调用,该如何是好呢?

func test(_ value: Int) {print(#function)
}func test<N: Numeric>(_ value: N) {print("泛型:\(#function)")
}// 如果希望调用泛型版本的重载,该如何是好?
test(11)

在 Swift 语言中,幕后秃头大佬们提供了一种机制,让我们可以“降低”某一种方法重载的“偏爱程度”。这可以通过在其方法定义前增加 @_disfavoredOverload 修饰符来搞定:

@_disfavoredOverload
func test(_ value: Int) {print(#function)
}func test<N: Numeric>(_ value: N) {print("泛型:\(#function)")
}// 现在会调用泛型版本的重载方法
test(11)

如上代码所示,我们向编译器挑明了小伙伴们更“讨厌”非泛型版本重载的强烈情感,所以编译器会非常知趣的选择泛型版本的重载调用。

最后再举一个“栗子”。对于以下 3 种 test() 方法的重载来说,默认会选择第一种重载;当第一种重载被 @_disfavoredOverload 修饰后,才会选择第二种重载;只有当前两种重载都被“嫌弃”之后,才轮到最后的泛型重载闪亮登场:

@_disfavoredOverload
func test(_ value: Int) {print(#function)
}@_disfavoredOverload
func test(_ values: Int...) {print("不定参数(Variadic):\(#function)")
}func test<N: Numeric>(_ value: N) {print("泛型:\(#function)")
}// 只有前两种重载都被“嫌弃”之后,才轮到最后一种重载!
test(11)

现在,小伙伴们对于 Swift 语言中多个重载方法到底该选谁?以及如何进一步“暗箱操作,逆天改命”修改默认重载规则的方法都了然于胸了吧!棒棒哒!💯


@_disfavoredOverload 修饰符前面有下划线,这意味着它是 Swift 语言的一个内部特性,在将来很可能会被丢弃,所以如果大家依赖于它则需要特别留意。

所幸的是,在最新 Swift 5.9 中 @_disfavoredOverload 仍然可用。


总结

在本篇博文中,我们先是介绍了 Swift 语言中当方法的多个重载同时满足调用语义时编译器的选择之道,并且随后进一步讨论了如何“逆天改命”修改这一默认的选择法则。

感谢观赏,再会!😎

这篇关于Swift 隐藏宝藏:“逆天改命”调整方法重载(function overloading)优先级的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/682312

相关文章

Springboot的配置文件及其优先级说明

《Springboot的配置文件及其优先级说明》文章介绍了SpringBoot的配置文件,包括application.properties和application.yml的使用,以及它们的优先级,还讨... 目录配置文件内置配置文件yml与properties的比较优先级比较外置配置文件springboot

自定义注解SpringBoot防重复提交AOP方法详解

《自定义注解SpringBoot防重复提交AOP方法详解》该文章描述了一个防止重复提交的流程,通过HttpServletRequest对象获取请求信息,生成唯一标识,使用Redis分布式锁判断请求是否... 目录防重复提交流程引入依赖properties配置自定义注解切面Redis工具类controller

Java调用DeepSeek API的8个高频坑与解决方法

《Java调用DeepSeekAPI的8个高频坑与解决方法》现在大模型开发特别火,DeepSeek因为中文理解好、反应快、还便宜,不少Java开发者都用它,本文整理了最常踩的8个坑,希望对... 目录引言一、坑 1:Token 过期未处理,鉴权异常引发服务中断问题本质典型错误代码解决方案:实现 Token

Nginx 访问控制的多种方法

《Nginx访问控制的多种方法》本文系统介绍了Nginx实现Web访问控制的多种方法,包括IP黑白名单、路径/方法/参数控制、HTTP基本认证、防盗链机制、客户端证书校验、限速限流、地理位置控制等基... 目录一、IP 白名单与黑名单1. 允许/拒绝指定IP2. 全局黑名单二、基于路径、方法、参数的访问控制

Python中Request的安装以及简单的使用方法图文教程

《Python中Request的安装以及简单的使用方法图文教程》python里的request库经常被用于进行网络爬虫,想要学习网络爬虫的同学必须得安装request这个第三方库,:本文主要介绍P... 目录1.Requests 安装cmd 窗口安装为pycharm安装在pycharm设置中为项目安装req

nginx跨域访问配置的几种方法实现

《nginx跨域访问配置的几种方法实现》本文详细介绍了Nginx跨域配置方法,包括基本配置、只允许指定域名、携带Cookie的跨域、动态设置允许的Origin、支持不同路径的跨域控制、静态资源跨域以及... 目录一、基本跨域配置二、只允许指定域名跨域三、完整示例四、配置后重载 nginx五、注意事项六、支持

MySQL查看表的历史SQL的几种实现方法

《MySQL查看表的历史SQL的几种实现方法》:本文主要介绍多种查看MySQL表历史SQL的方法,包括通用查询日志、慢查询日志、performance_schema、binlog、第三方工具等,并... 目录mysql 查看某张表的历史SQL1.查看MySQL通用查询日志(需提前开启)2.查看慢查询日志3.

MySQL底层文件的查看和修改方法

《MySQL底层文件的查看和修改方法》MySQL底层文件分为文本类(可安全查看/修改)和二进制类(禁止手动操作),以下按「查看方法、修改方法、风险管控三部分详细说明,所有操作均以Linux环境为例,需... 目录引言一、mysql 底层文件的查看方法1. 先定位核心文件路径(基础前提)2. 文本类文件(可直

Java实现字符串大小写转换的常用方法

《Java实现字符串大小写转换的常用方法》在Java中,字符串大小写转换是文本处理的核心操作之一,Java提供了多种灵活的方式来实现大小写转换,适用于不同场景和需求,本文将全面解析大小写转换的各种方法... 目录前言核心转换方法1.String类的基础方法2. 考虑区域设置的转换3. 字符级别的转换高级转换

使用Python实现局域网远程监控电脑屏幕的方法

《使用Python实现局域网远程监控电脑屏幕的方法》文章介绍了两种使用Python在局域网内实现远程监控电脑屏幕的方法,方法一使用mss和socket,方法二使用PyAutoGUI和Flask,每种方... 目录方法一:使用mss和socket实现屏幕共享服务端(被监控端)客户端(监控端)方法二:使用PyA