常量

什么是字面量

考虑下面代码:

var a int = 50
var b string = "I love Go"
var c string = b

这里变量a赋值为字面量50,变量b赋值为字面量I love Go,而变量c则赋值为变量b。像5, -89I love Go67.89等,在编程语言中称为字面量。

什么是常量

常量是相对于变量而言的,考虑下面代码:

var a int = 50
const b int = 50

这里两个量ab虽然都是声明为int类型,并且赋值为50,但是a是变量,b是常量。那么常量与变量有什么区别呢?答案是常量不可重新被赋值。

package main

func main() {
    const a = 55
    a = 89 // 给常量重新赋值会报错
}

Go Playground在线运行 这里由于a被声明为了const常量,因此后面再进行赋值会报错。

常量跟变量的另外一个不同点是,常量声明的同时需要赋值,因此下面的写法会报错:

package main

func main() {
    const int a // 先声明
    a = 50 // 再赋值
}

Go Playground在线运行

另外需要注意,常量值需要在编译时确定,因此不能将函数调用的返回值赋值给常量。

package main

import (  
    "fmt"
    "math"
)

func main() {  
    fmt.Println("Hello, playground")
    var a = math.Sqrt(4)    // allowed
    const b = math.Sqrt(4)  // not allowed
}

Go Playground在线运行

该程序中,a是变量,因此可以赋值为函数调用返回值;但是b是常量,而函数math.Sqrt(4)的执行是运行时,因此会报错。

无类型常量和有类型常量

Golang中每个变量都有对应的类型,比如:

var a int = 10
var b = 20
c := 30

这三种不同的写法,声明的变量abc均为int类型,虽然有显示声明的,有Golang自动推断的,有简写形式。

Golang中的常量与变量不同,常量包括两种:untyped常量(无类型常量)和typed常量(有类型常量)。为了方便理解,这里以香水为例来说明下。我们都知道,兰蔻(Lancome)和欧碧泉(Biotherm)都属于欧莱雅集团,假如他们公司新发明了一款香水:邂逅

package main

import "fmt"

type Lancome string
type Biotherm string


func main() {
    const perfume = "邂逅" // 无类型常量
    const perfume1 Lancome  = "邂逅" // 有类型常量,类型是兰蔻
    const perfume2 Biotherm = "邂逅" // 有类型常量,类型是欧碧泉
    var perfume3 Lancome = perfume // 合法
    perfume3 = perfume1 // 合法
    perfume3 = perfume2 // 不合法
    fmt.Println(perfume, perfume1, perfume2, perfume3)
}

Go Playground在线运行 这段程序中,首先声明的常量perfume就是无类型常量,而后面声明的perfume1perfume2则均是有类型常量,其常量类型分别是LancomeBiotherm。虽然都是常量,但是用法却不同,后面声明的变量perfume3Lancome类型,因此只能赋值为同类型常量perfume1或者无类型常量perfume

为了加深理解我们继续深挖香水这个例子。虽然邂逅这款香水是欧莱雅集团出品的,但是在贴标之前是没有品牌属性的,即const perfume = "邂逅",这行代码我们 并未给perfume常量贴标,因此不属于任何品牌,属于无类型常量。但是一旦贴标,比如const perfume2 Biotherm = "邂逅",这行代码我们给perfume2常量贴标,贴上Biotherm品牌标志,即变为有类型常量。有类型常量是贴标产品,因此不能随便再撕下标签,贴上其他品牌的标志,比如perfume3 = perfume2,因此这行代码是不合法的。

初看这个特性难以理解,但是这样使得Golang的常量更加灵活,我们首先声明无类型的常量,后面用起来就会更加灵活,只要类型兼容,想赋值给什么变量就赋值给什么变量。这里再解释下这里所说的类型兼容,比如const str = "邂逅"中无类型常量str本质是一个字符串,与类型stringLancome均兼容,这里的类型兼容用来描述无类型常量与其他类型的兼容性:

type Lancome string

const str = "邂逅"
var str1 string = str
var perfume1 Lancome = str

字符串常量

字符串常量声明如下:

const hello = "Hello World"

参考前面对无类型常量和有类型常量的解释,这里声明了一个无类型字符串常量hello。 看下下面的代码你可能会疑惑,为什么说hello是无类型的呢,明明打印出了string

package main

import (  
    "fmt"
)

func main() {  
    const name = "Sam"
    fmt.Printf("type %T value %v", name, name)

}

Go Playground在线运行

运行结果如下:

type string value Sam

这里解释下。前面之所以打印出了string,并不是说常量name是Golang的string类型。本质原因是因为这里name是作为了fmt.Printf函数的参数,而该函数这里期望接收string类型,又因为常量"Sam"本身是一个字符串,与string类型兼容,因此才打印出了string

布尔常量

布尔常量没什么特殊的,很简答,这里仅举个例子:

package main

func main() {  
    const trueConst = true
    type myBool bool
    var defaultBool = trueConst //allowed
    var customBool myBool = trueConst //allowed
    defaultBool = customBool //not allowed
}

Go Playground在线运行 这里由于变量defaultBool是布尔类型,因此可被赋值无类型常量tureConst,但是不能被赋值为类型不相同的有类型常量customBool

数值常量

数值常量包括整型、浮点型以及复数型。

package main

import (  
    "fmt"
)

func main() {  
    const a = 5
    var intVar int = a
    var int32Var int32 = a
    var float64Var float64 = a
    var complex64Var complex64 = a
    fmt.Println("intVar",intVar, "\nint32Var", int32Var, "\nfloat64Var", float64Var, "\ncomplex64Var",complex64Var)
}

Go Playground在线运行 这里常量a是无类型的,值为5,由于是无类型的,因此可以赋值为多种兼容的类型,执行结果如下:

intVar 5 
int32Var 5 
float64Var 5 
complex64Var (5+0i)

从下面的程序我们可以看出整型变量、浮点型变量以及复数型变量被赋值常量时的默认类型分别是intfloat64complex128

package main

import (  
    "fmt"
)

func main() {  
    var i = 5
    var f = 5.6
    var c = 5 + 6i
    fmt.Printf("i's type %T, f's type %T, c's type %T", i, f, c)

}

Go Playground在线运行 执行结果如下:

i's type int, f's type float64, c's type complex128

数值表达式

数值型常量声明时也不可以使用函数调用返回值来初始化,但是可以使用数值表达式来初始化,比如:

package main

import (
    "fmt"
)

func main() {
    const a float64 = 5.9 / 5.9
    fmt.Printf("a's type %T value %v", a, a)
}

Go Playground在线运行

执行结果如下:

a's type float64 value 1

Last updated