5. 常量
定義
在 Go 語言中,術語"常量"用於表示固定的值。比如 5
、-89
、 I love Go
、67.89
等等。
看看下面的代碼:
var a int = 50
var b string = "I love Go"
在上面的代碼中,變量 a 和 b 分別被賦值為常量 50 和 I love GO。關鍵字 const
被用於表示常量,比如 50
和 I love Go
。即使在上面的代碼中我們沒有明確的使用關鍵字 const
,但是在 Go 的內部,它們是常量。
顧名思義,常量不能再重新賦值為其他的值。因此下面的程序將不能正常工作,它將出現一個編譯錯誤: cannot assign to a.
。
package main
func main() {
const a = 55 // 允許
a = 89 // 不允許重新賦值
}
常量的值會在編譯的時候確定。因為函數調用發生在運行時,所以不能將函數的返回值賦值給常量。
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println("Hello, playground")
var a = math.Sqrt(4) // 允許
const b = math.Sqrt(4) // 不允許
}
在上面的程序中,因為 a
是變量,因此我們可以將函數 math.Sqrt(4)
的返回值賦值給它(我們將在單獨的地方詳細討論函數)。
b
是一個常量,它的值需要在編譯的時候就確定。函數 math.Sqrt(4)
只會在運行的時候計算,因此 const b = math.Sqrt(4)
將會拋出錯誤 error main.go:11: const initializer math.Sqrt(4) is not a constant)
字符串常量
雙引號中的任何值都是 Go 中的字符串常量。例如像 Hello World
或 Sam
等字符串在 Go 中都是常量。
什么類型的字符串屬於常量?答案是他們是無類型的。
像 Hello World
這樣的字符串常量沒有任何類型。
const hello = "Hello World"
上面的例子,我們把 Hello World
分配給常量 hello
。現在常量 hello
有類型嗎?答案是沒有。常量仍然沒有類型。
Go 是一門強類型語言,所有的變量必須有明確的類型。那么, 下面的程序是如何將無類型的常量 Sam
賦值給變量 name
的呢?
package main
import (
"fmt"
)
func main() {
var name = "Sam"
fmt.Printf("type %T value %v", name, name)
}
答案是無類型的常量有一個與它們相關聯的默認類型,並且當且僅當一行代碼需要時才提供它。在聲明中 var name = "Sam" , name 需要一個類型,它從字符串常量 Sam 的默認類型中獲取。
有沒有辦法創建一個帶類型的常量?答案是可以的。以下代碼創建一個有類型常量。
const typedhello string = "Hello World"
上面代碼中, typedhello
就是一個 string
類型的常量。
Go 是一個強類型的語言,在分配過程中混合類型是不允許的。讓我們通過以下程序看看這句話是什么意思。
package main
func main() {
var defaultName = "Sam" // 允許
type myString string
var customName myString = "Sam" // 允許
customName = defaultName // 不允許
}
在上面的代碼中,我們首先創建一個變量 defaultName
並分配一個常量 Sam
。常量 Sam 的默認類型是 string ,所以在賦值后 defaultName 是 string 類型的。
下一行,我們將創建一個新類型 myString
,它是 string
的別名。
然后我們創建一個 myString
的變量 customName
並且給他賦值一個常量 Sam
。因為常量 Sam
是無類型的,它可以分配給任何字符串變量。因此這個賦值是允許的,customName
的類型是 myString
。
現在,我們有一個類型為 string
的變量 defaultName
和另一個類型為 myString
的變量 customName
。即使我們知道這個 myString
是 string
類型的別名。Go 的類型策略不允許將一種類型的變量賦值給另一種類型的變量。因此將 defaultName
賦值給 customName
是不允許的,編譯器會拋出一個錯誤 main.go:7:20: cannot use defaultName (type string) as type myString in assignmen
。
布爾常量
布爾常量和字符串常量沒有什么不同。他們是兩個無類型的常量 true
和 false
。字符串常量的規則適用於布爾常量,所以在這里我們不再重復。以下是解釋布爾常量的簡單程序。
package main
func main() {
const trueConst = true
type myBool bool
var defaultBool = trueConst // 允許
var customBool myBool = trueConst // 允許
defaultBool = 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)
}
上面的程序,常量 a
是沒有類型的,它的值是 5
。您可能想知道 a
的默認類型是什么,如果它確實有一個的話, 那么我們如何將它分配給不同類型的變量。答案在於 a
的語法。下面的程序將使事情更加清晰。
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)
}
在上面的程序中,每個變量的類型由數字常量的語法決定。5
在語法中是整數, 5.6
是浮點數,5+6i
的語法是復數。當我們運行上面的程序,它會打印出 i's type int, f's type float64, c's type complex128
。
現在我希望下面的程序能夠正確的工作。
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)
}
在這個程序中, a
的值是 5
,a
的語法是通用的(它可以代表一個浮點數、整數甚至是一個沒有虛部的復數),因此可以將其分配給任何兼容的類型。這些常量的默認類型可以被認為是根據上下文在運行中生成的。 var intVar int = a
要求 a
是 int
,所以它變成一個 int
常量。 var complex64Var complex64 = a
要求 a
是 complex64
,因此它變成一個復數類型。很簡單的:)。
數字表達式
數字常量可以在表達式中自由混合和匹配,只有當它們被分配給變量或者在需要類型的代碼中的任何地方使用時,才需要類型。
package main
import (
"fmt"
)
func main() {
var a = 5.9/8
fmt.Printf("a's type %T value %v",a, a)
}
在上面的程序中, 5.9
在語法中是浮點型,8
是整型,5.9/8
是允許的,因為兩個都是數字常量。除法的結果是 0.7375
是一個浮點型,所以 a
的類型是浮點型。這個程序的輸出結果是: a's type float64 value 0.7375
。