hello云胜

技术与生活

0%

Go语言中的常量

今天记录点的轻松的内容,常量。常量在在一门语言中算是比较简单的内容。但是go语言在常量设计上有一些创新,还挺有趣的。

  • 支持无类型常量
  • 支持隐式类型转换
  • 可用于实现枚举

Go语言引入const关键字来声明变量。

不像java,没有常量的关键字。但是java有enum定义枚举,go没有枚举类型的关键字,通过常量来实现。

无类型常量

首先定义常量和用var声明变量的语法是一样的。

可以显式指定类型,如

1
2
3
4
5
6
const a int = 100
const b int = a + 10

func main() {
fmt.Println(a + b)
}

要注意,go是强类型语言。即使底层类型一致,不同类型也不可以直接进行运算。必须进行显式的类型转换。如:

1
2
3
4
5
type myInt int

const a myInt = 100
const b int = a + 10 // 编译报错:cannot use a + 10 (constant 110 of type myInt) as int value in constant
const b int = int(a) + 10 // ok

但是如果定义常量的时候不指定类型,就是无类型常量

1
2
3
4
5
type myInt int
const a myInt = 100
// 无类型常量,可以编译
const n = 7
const b myInt = a + n

这时候用到无类型常量的另一个特性:隐式类型转换

隐式类型转换

无类型常量也不是说就真的没有类型,它也有自己的默认类型,不过它的默认类型 是根据它的初值形式来决定的。像上面代码中的常量 n 的初值为整数形式,所以它的默认 类型为 int。

这样还是有问题的,int和myInt类型的数据不可以直接进行运算。

所以对于无类型常量参与的表达式求值,Go 编译器会根据上下文中的类型 信息,把无类型常量自动转换为相应的类型后,再参与求值计算。也就是会把n转换为myInt类型。

实现枚举

Go没有原生提供枚举类型。枚举类型 本质上就是一个由有限数量常量所构成的集合,所以用const实现枚举并没有什么问题。

1
2
3
4
5
6
// 枚举
const (
MONDAY = 1
TUESDAY = 2
WEDNESDAY = 3
)

和常量定义并没有什么区别。

通常这里应该介绍下iota了,但说实话,我并不喜欢iota。

iota称为行偏移量指 示器。

go的枚举有一个特性是自动重复上一行,比如

1
2
3
4
5
const (
MONDAY = 1
TUESDAY
WEDNESDAY
)

那么TUESDAY和WEDNESDAY的值就都是1

这种情况下可以使用iota

1
2
3
4
5
const (
MONDAY = 1 + iota
TUESDAY
WEDNESDAY
)

在每一个const块中,iota的初始值是0,并且每一行代码的iota的值依次递增。所以上述代码等价于

1
2
3
4
5
const (
MONDAY = 1 + 0
TUESDAY = 1 + 1
WEDNESDAY = 1 + 2
)

如果想跳过某个iota值,可以使用_

1
2
3
4
5
6
const (
MONDAY = 1 + iota
- // iota == 1被跳过了
TUESDAY // 3
WEDNESDAY // 4
)

再注意一点,iota是每一行的值依次递增,也就是说于同一行的 iota 即便出现多次,多个 iota 的值也是一样的

1
2
3
4
5
6
const (
MONDAY = 1 + iota + iota + iota // 这三个iota都是0
- // iota == 1被跳过了
TUESDAY // 3
WEDNESDAY // 4
)

iota看起来挺灵活的,并且在go标准库或者很多知名的开源项目中都有使用。但是在我们平时的开发中还是习惯将常量明确的显式的定义出来。因为常量可读性我认为是更加重要的。我可不想看到这个常量定义,再去算算这个常量到底是多少。

还要注意,在已确定的常量定义中插入一行,会导致下面所有常量的值改变。

比如:

1
2
3
4
5
6
const (
SUNDAY = 1 + iota
MONDAY
TUESDAY
WEDNESDAY
)

和java的enum相比,表现力还是差了很多。