Skip to content

Go 支持运算符重载吗?

June 15, 2022 | 05:46 AM

假设有一个学生结构体(面向对象中的类),拥有名字和年龄两个字段。现在想仅根据年龄的大小来判断学生的大小和相等关系。这个需求很简单,一个方法就可以搞定。那是否可以用 = != > < >= <= 这四个运算符来直接对实例进行比较呢?

先看一下 Python 中怎样实现:

class Student:
    def __init__(self, name: str, score: int) :
        self.name = name
        self.score = score

    def __eq__(self, other):  # equal
        if isinstance(other, Student):
            return self.score == other.score
        else:
            raise TypeError(type(other))

    def __lt__(self, other):  # less than
        if isinstance(other, Student):
            return self.score < other.score
        else:
            raise TypeError(type(other))

    def __gt__(self, other):  # greater than
        if isinstance(other, Student):
            return self.score > other.score
        else:
            raise TypeError(type(other))

    def __le__(self, other):  # less equal
        if isinstance(other, Student):
            return self.score <= other.score
        else:
            raise TypeError(type(other))

    def __ge__(self, other):  # greater equal
        if isinstance(other, Student):
            return self.score >= other.score
        else:
            raise TypeError(type(other))

if __name__ == '__main__':
    s1 = Student("Paul", 88)
    s2 = Student("Tom", 88)
    print(s1 >= s2)
    print(s1 <= s2)
    print(s1 < s2)
    print(s1 == s2)

借助双下方法,我们可以轻松实现。

这种对『运算符赋予多重含义,使同一个运算符在作用于不同类型数据时产生不同行为』的现象,被称为运算符重载

那么回到题目中提出的问题,Go 中可以实现运算符重载吗?

答案是不能,但是也不是完全不能。

『不能』是因为,Go 不允许对结构体使用 > 等大小运算符,也就更不能自定义结构体的大小关系了。

『不是完全不能』是因为,尽管无法自定义结构体的大小,但是我们却可以重载 = != ,对结构体的相等性做出判断。要求也很简单,只要结构体中所有字段类型都是可以用 = != 的就行。上述特性被称为可比较性(comparable) ,而之前说到的 > < >= <= 被称为顺序性(ordered)。下面是不同类型可比较性和顺序性的汇总。

Go Comparable Type

图片来源:深入理解 Go Comparable Type | SORCERERXW


也就是说,尽管我们不能对学生结构体做大小判断,也不能自定义两个学生的相等性,但是我们还是可以使用 = != 的,只要结构体中字段类型都是可比较的。如果学生结构体两个实例中所有字段值相等,那么 Go 就认为这两个实例相等。

示例程序如下:

package main

import "fmt"

type Student struct {
    name string
    score int
}

func main() {
    s1 := Student{"Paul", 88}
    s2 := Student{"Tom", 78}
    s3 := Student{"Paul", 88}

    fmt.Println(s1 == s2)  // false
    fmt.Println(s1 == s3)  // true
}

而这里说到的可比较性其实也是 map 键值的要求:map 只能用可比较的类型作为键。更多信息可以参考延伸阅读第二条。

结论

尽管 Go 不允许运算符重载,但是对于所有字段都是可比较的结构体来说,却可以重载 = != ,当所有字段的值都相等时,Go 认为实例相等。

延伸阅读