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

相关文章

Python安装Pandas库的两种方法

《Python安装Pandas库的两种方法》本文介绍了三种安装PythonPandas库的方法,通过cmd命令行安装并解决版本冲突,手动下载whl文件安装,更换国内镜像源加速下载,最后建议用pipli... 目录方法一:cmd命令行执行pip install pandas方法二:找到pandas下载库,然后

Linux系统中查询JDK安装目录的几种常用方法

《Linux系统中查询JDK安装目录的几种常用方法》:本文主要介绍Linux系统中查询JDK安装目录的几种常用方法,方法分别是通过update-alternatives、Java命令、环境变量及目... 目录方法 1:通过update-alternatives查询(推荐)方法 2:检查所有已安装的 JDK方

SQL Server安装时候没有中文选项的解决方法

《SQLServer安装时候没有中文选项的解决方法》用户安装SQLServer时界面全英文,无中文选项,通过修改安装设置中的国家或地区为中文中国,重启安装程序后界面恢复中文,解决了问题,对SQLSe... 你是不是在安装SQL Server时候发现安装界面和别人不同,并且无论如何都没有中文选项?这个问题也

Java Thread中join方法使用举例详解

《JavaThread中join方法使用举例详解》JavaThread中join()方法主要是让调用改方法的thread完成run方法里面的东西后,在执行join()方法后面的代码,这篇文章主要介绍... 目录前言1.join()方法的定义和作用2.join()方法的三个重载版本3.join()方法的工作原

在MySQL中实现冷热数据分离的方法及使用场景底层原理解析

《在MySQL中实现冷热数据分离的方法及使用场景底层原理解析》MySQL冷热数据分离通过分表/分区策略、数据归档和索引优化,将频繁访问的热数据与冷数据分开存储,提升查询效率并降低存储成本,适用于高并发... 目录实现冷热数据分离1. 分表策略2. 使用分区表3. 数据归档与迁移在mysql中实现冷热数据分

Spring Boot从main方法到内嵌Tomcat的全过程(自动化流程)

《SpringBoot从main方法到内嵌Tomcat的全过程(自动化流程)》SpringBoot启动始于main方法,创建SpringApplication实例,初始化上下文,准备环境,刷新容器并... 目录1. 入口:main方法2. SpringApplication初始化2.1 构造阶段3. 运行阶

Olingo分析和实践之ODataImpl详细分析(重要方法详解)

《Olingo分析和实践之ODataImpl详细分析(重要方法详解)》ODataImpl.java是ApacheOlingoOData框架的核心工厂类,负责创建序列化器、反序列化器和处理器等组件,... 目录概述主要职责类结构与继承关系核心功能分析1. 序列化器管理2. 反序列化器管理3. 处理器管理重要方

Python错误AttributeError: 'NoneType' object has no attribute问题的彻底解决方法

《Python错误AttributeError:NoneTypeobjecthasnoattribute问题的彻底解决方法》在Python项目开发和调试过程中,经常会碰到这样一个异常信息... 目录问题背景与概述错误解读:AttributeError: 'NoneType' object has no at

postgresql使用UUID函数的方法

《postgresql使用UUID函数的方法》本文给大家介绍postgresql使用UUID函数的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录PostgreSQL有两种生成uuid的方法。可以先通过sql查看是否已安装扩展函数,和可以安装的扩展函数

Java中Arrays类和Collections类常用方法示例详解

《Java中Arrays类和Collections类常用方法示例详解》本文总结了Java中Arrays和Collections类的常用方法,涵盖数组填充、排序、搜索、复制、列表转换等操作,帮助开发者高... 目录Arrays.fill()相关用法Arrays.toString()Arrays.sort()A