枚举类型
本节介绍仓颉中的 enum
类型。enum
类型提供了通过列举一个类型的所有可能取值来定义此类型的方式。
在很多语言中都有 enum
类型(或者称枚举类型),但是不同语言中的 enum
类型的使用方式和表达能力均有所差异,仓颉中的 enum
类型可以理解为函数式编程语言中的代数数据类型(Algebraic Data Types)。
接下来,首先介绍如何定义和使用 enum
,然后介绍如何使用模式匹配使得 enum
取不同值时执行不同的操作,最后介绍一个名为 Option
的常用 enum
类型,用于表示某个类型的实例要么有值要么没值。
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
和struct
类型存在互递归关系时,且enum
类型作为Option
的类型参数,可能存在编译错误。
enum 的使用
定义了 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