本节介绍仓颉中的 enum 类型。enum 类型提供了通过列举一个类型的所有可能取值来定义此类型的方式。
在很多语言中都有 enum 类型(或者称枚举类型),但是不同语言中的 enum 类型的使用方式和表达能力均有所差异,仓颉中的 enum 类型可以理解为函数式编程语言中的代数数据类型(Algebraic Data Types)。
接下来,首先介绍如何定义和使用 enum,然后介绍如何使用模式匹配使得 enum 取不同值时执行不同的操作,最后介绍一个名为 Option 的常用 enum 类型,用于表示某个类型的实例要么有值要么没值。
定义 enum 时需要把它所有可能的取值一一列出,我们称这些值为 enum 的构造器(或者 constructor)。
enum RGBColor {
| Red | Green | Blue
} enum 类型的定义以关键字 enum 开头,接着是 enum 的名字,之后是定义在一对花括号中的 enum 体,enum 体中定义了若干构造器,多个构造器之间使用 | 进行分隔(第一个构造器之前的 | 是可选的)。上例中定义了一个名为 RGBColor 的 enum 类型,它有 3 个构造器:Red、Green 和 Blue,分别表示 RGB 色彩模式中的红色、绿色和蓝色。
上述 enum 中的构造器还可以携带若干(至少一个)参数,称为有参构造器。例如,可以为 Red、Green 和 Blue 设置一个 UInt8 的类型的参数,用来表示每个颜色的亮度级别:
enum RGBColor {
| Red(UInt8) | Green(UInt8) | Blue(UInt8)
} 仓颉支持同一个 enum 中定义多个同名构造器,但是要求这些构造器的参数个数不同(认为没有参数的构造器的参数个数等于 0),例如:
enum RGBColor {
| Red | Green | Blue
| Red(UInt8) | Green(UInt8) | Blue(UInt8)
} enum 支持递归定义,例如,下面的例子中使用 enum 定义了一种表达式(即 Expr),此表达式只能有 3 种形式:单独的一个数字 Num(携带一个 Int64 类型的参数)、加法表达式 Add(携带两个 Expr 类型的参数)、减法表达式 Sub(携带两个 Expr 类型的参数)。对于 Add 和 Sub 这两个构造器,其参数中递归地使用到了 Expr 自身。
enum Expr {
| Num(Int64)
| Add(Expr, Expr)
| Sub(Expr, Expr)
} 另外,在 enum 体中还可以定义一系列成员函数、操作符函数(详见操作符重载章节)和成员属性(详见属性章节),但是要求构造器、成员函数、成员属性之间不能重名。例如,下面的例子在 RGBColor 中定义了一个名为 printType 的函数,它会输出字符串 RGBColor:
enum RGBColor {
| Red | Green | Blue
public static func printType() {
print("RGBColor")
}
}
定义了 enum 类型之后,就可以创建此类型的实例(即 enum 值),enum 值只能取 enum 类型定义中的一个构造器。enum 没有构造函数,可以通过 类型名.构造器,或者直接使用构造器的方式来构造一个 enum 值(对于有参构造器,需要传实参)。
下例中,RGBColor 中定义了三个构造器,其中有两个无参构造器(Red 和 Green)和一个有参构造器(Blue(UInt8)),main 中定义了三个 RGBColor 类型的变量 r,g 和 b,其中,r 的值使用 RGBColor.Red 进行初始化,g 的值直接使用 Green 进行初始化,b 的值使用 Blue(100) 进行初始化:
enum RGBColor {
| Red | Green | Blue(UInt8)
}
main() {
let r = RGBColor.Red
let g = Green
let b = Blue(100)
} 当省略类型名时,enum 构造器的名字可能和类型名、变量名、函数名发生冲突。此时必须加上 enum 类型名来使用 enum 构造器,否则只会选择同名的类型、变量、函数定义。
下面的例子中,只有构造器 Blue(UInt8) 可以不带类型名使用,Red 和 Green(UInt8) 皆会因为名字冲突而不能直接使用,必须加上类型名 RGBColor。
let Red = 1
func Green(g: UInt8) {
return g
}
enum RGBColor {
| Red | Green(UInt8) | Blue(UInt8)
}
let r1 = Red // Will choose 'let Red'
let r2 = RGBColor.Red // Ok: constructed by enum type name
let g1 = Green(100) // Will choose 'func Green'
let g2 = RGBColor.Green(100) // Ok: constructed by enum type name
let b = Blue(100) // Ok: can be uniquely identified as an enum constructor 如下的例子中,只有构造器 Blue 会因为名称冲突而不能直接使用,必须加上类型名 RGBColor。
class Blue {}
enum RGBColor {
| Red | Green(UInt8) | Blue(UInt8)
}
let r = Red // Ok: constructed by enum type name
let g = Green(100) // Ok: constructed by enum type name
let b = Blue(100) // Will choose constructor of 'class Blue' and report an error