6.2 运算符的重载与自定义

准确来说,重载运算符和自定义运算符都是开发者自定义运算符功能的手段,二者的差别在于重载运算符是给系统已经存在的运算符追加新的功能,而自定义运算符是完全自定义一个系统不存在的运算符来实现特定的运算功能。

6.2.1 重载运算符

读者在认识重载运算符前,首先应该清楚重载的概念。重载的概念最初是针对函数的,对同一个函数名,设置不同的参数类型以实现不同的功能被称为函数的重载。在Objective-C中函数参数的名称是包含在函数名里的,因此从严格意义上讲,Objective-C语言并不存在函数的重载操作。Swift语言则不同,我们可以通过对函数重载应用的一个小例子来理解重载的意义。

实现一个整数的加法函数十分简单,示例如下:

上面的示例代码用来进行整型数据的加法是完全可以的,但是如果需要进行浮点型数据的加法就会出现问题,开发者如果直接将浮点数传入addFunc()函数中,编译器就会直接报类型错误,这时你可能想要创建一个针对浮点数的加法运算,示例如下:

这样是解决了问题,但是这种设计思路十分糟糕,实现相同功能的函数,由于参数的不同被生生切成了两个,其实开发者可以使用相同的函数名addFunc(),通过重载实现不同类型参数的计算,示例如下:

如上代码,通过重载的方式对不同数据类型实现了加法操作,并且在调用加法函数的时候,开发者只需要记住这一个函数名即可,这就大大增强了代码的统一性。

类比于函数的重载,运算符的重载是指在系统已经存在的运算符上扩展新的功能。其实在前面章节中使用的加号运算符“+”就是通过重载实现的,开发者可以直接使用“+”运算符进行整型数据、浮点型数据甚至字符串类型数据的相加操作。下面我们通过自定义一个圆形的类,通过重载加号运算符“+”来实现支持对圆形类实例的相加操作。

设计圆形类如下,其中有两个属性,分别表示圆形半径与圆心:

定义两个Circle实例进行相加操作时,应执行这样的运算:两个Circle实例相加返回一个新的Circle实例,并且这个新的Circle实例的圆心为第一个Circle操作数的圆心,新的Circle实例的半径为两个操作数Cirlcle实例半径的和,重载加法运算符如下:

可以发现,重载运算符的语法格式与函数十分相似。实际上,运算符就是通过函数的方式定义的。

提示

在某些场景下,运算符也的确可以像函数一样来使用,例如在一个函数参数中传入闭包时,也可以直接传入某个功能类型的运算符,示例如下:

func myFunc(closure:(Circle,Circle)->Circle) {
}
//将重载的加法运算符传入
myFunc(closure: +)

注意

Swift语言中还有一个覆写的概念。覆写是指子类对父类中的属性和方法进行适合自身的重新实现,和重载意义完全不同,读者在后面的学习中会遇到,注意不要将这两个概念混淆。

6.2.2 自定义运算符

重载运算符是为已经存在的系统运算符扩展新的功能。开发者也可以通过自定义系统不存在的运算符来实现特殊的需求,例如Swift语言从2.2版本开始移除了“++”“--”运算符,这里我们可以通过自定义运算符来添加自加运算符“++”,示例如下:

自定义运算符分为两个步骤,首先开发者需要将要定义的运算符进行声明,如上代码中的prefix operator ++。在声明运算符的结构中,prefix的作用是运算符的类型,可以使用prefix关键字将其声明为前缀运算符,也可以使用infix关键字将其声明为中缀运算符、postfix关键字将其声明为后缀运算符。在进行运算符的实现时,后缀和前缀运算符只能有一个参数,参数在func关键字前需要表明要实现的运算符类型,而中缀运算符需要有两个参数且func关键字前不需要额外标明,示例如下:

提示

前缀运算符是指在只有一个操作数且在使用运算符进行运算时,运算符需要出现在操作数的前面;中缀运算符需要有两个操作数,且在进行运算时运算符需要出现在两个操作数的中间;后缀运算符只能有一个操作数,在运算时后缀运算符需要出现在操作数的后面。

需要注意,Swift语言中提供了许多Unicode字符,可用于运算符的自定义,但是也有一些规则,自定义运算符常使用如下字符作为开头:/、=、-、+、!、*、%、<、>、&、|、^、?、~。开发者也可以使用点“.”来进行运算符的定义。当开发者的自定义运算符中有使用到符号“.”的时候需要注意:如果“.”出现在自定义运算符的开头,则运算符中可以出现多个符号“.”,例如“.+.”;如果自定义运算符中的符号“.”不在开头,那么这个自定义运算符中只允许出现一个符号“.”。

提示

Swift语言中也有一些保留符号,它们不可以单独被重载和自定义。保留符号为=、->、//、/*、*/、.、<、>、&、?、!。