Go型別全解:常數與變數大全!

2023-09-09 21:00:23

本篇文章深入探討了 Go 語言中型別確定值、型別不確定值以及對應型別轉換的知識點,後續充分解析了常數與變數及其高階用法,並舉出豐富的案例。

關注公眾號【TechLeadCloud】,分享網際網路架構、雲服務技術的全維度知識。作者擁有10+年網際網路服務架構、AI產品研發經驗、團隊管理經驗,同濟本復旦碩,復旦機器人智慧實驗室成員,阿里雲認證的資深架構師,專案管理專業人士,上億營收AI產品研發負責人。

一、型別確定值

型別確定值是與特定資料型別明確繫結的。型別確定值在 Go 中佔有很大一部分領域,包括但不限於常數、變數、函數返回值、結構體欄位等。下面是對型別確定值的的範例:

型別確定值在變數宣告中

當你在變數宣告中明確指定了型別,那麼這個變數就是型別確定值。

var x int = 10 // x 是型別確定值,型別為 int
var y string = "Hello" // y 是型別確定值,型別為 string

型別確定值在函數返回值中

函數也可以返回型別確定值。

func sum(a int, b int) int { // 返回值型別明確為 int
    return a + b
}

型別確定值在結構體欄位中

結構體欄位也是型別確定值。

type Person struct {
    Name string // Name 是型別確定值,型別為 string
    Age  int    // Age 是型別確定值,型別為 int
}

型別確定值在陣列和切片中

陣列和切片的元素也是型別確定值。

var arr [3]int = [3]int{1, 2, 3} // arr 是型別確定值,型別為 [3]int
var s []string = []string{"a", "b", "c"} // s 是型別確定值,型別為 []string

型別確定值在介面和型別斷言中

當你通過型別斷言將介面值轉換為某個具體型別時,結果是型別確定值。

var i interface{} = "this is string"
str, ok := i.(string) // str 是型別確定值,型別為 string

型別確定值在型別別名和自定義型別中

即使你建立了一個型別別名或自定義型別,實際上還是與原始型別繫結的型別確定值。

type Length int
var l Length = 10 // l 是型別確定值,型別為 Length

這些例子展示了型別確定值如何滲透到 Go 語言的各個方面,為開發者提供了嚴格的型別安全性。在編寫程式碼時,對型別有明確的瞭解可以幫助你編寫更健壯、更安全的程式。


二、型別不確定值

型別不確定值是沒有明確型別的值。這些值不是與任何具體的資料型別繫結的,因此在編譯時,Go 編譯器會根據上下文來推斷其型別。在 Go 中,型別不確定值是一個相當有趣和多面性的概念。這些值存在於常數宣告、算術運算、以及一些內建函數中。下面是對型別不確定值的的範例:

在算術運算中的型別不確定值

當你使用型別不確定值進行算術運算時,結果也是型別不確定值,除非運算中有一個型別確定值,這種情況下,結果將是型別確定值。

const c = 3  // 型別不確定值
var d int = 2  // 型別確定值

// e 依然是型別不確定值,因為參與運算的兩個值都是型別不確定值
const e = c * c 

// f 是型別確定值,因為參與運算的有一個型別確定值
var f = c * d 

在內建函數中的型別不確定值

一些 Go 的內建函數(如 len)返回型別不確定值。

const s = "hello world"
const l = len(s)  // l 是型別不確定值

型別不確定值與預設型別

每個型別不確定值都有一個與之關聯的預設型別,通常是基於字面量或者運算表示式。

const g = 42.0  // 預設型別是 float64
const h = 'x'   // 預設型別是 rune

型別不確定值在陣列長度宣告中

在陣列長度宣告中也可使用型別不確定值。

const size = 4
var arr [size]int  // 編譯器推斷 size 為 int

型別不確定值與 iota

iota 也是型別不確定值,經常用於列舉。

const (
    zero = iota  // zero 是 0
    one         // one 是 1
)

三、顯式型別轉換與型別推斷

在 Go 語言中,型別轉換和型別推斷是兩個相當重要的概念。它們共同定義瞭如何在編譯和執行時處理不同型別的值。我們將詳細討論這兩個概念,並通過範例來闡明其用法和重要性。

顯式型別轉換

顯式型別轉換是通過語法明確地將一個資料型別轉換為另一個資料型別的操作。

定義

在 Go 中,顯式型別轉換的語法是 T(v),其中 T 是目標型別,而 v 是要轉換的值。

var x float64 = 42.0
var y int = int(x)  // 顯式地將 float64 轉換為 int

限制與約束

顯式型別轉換並不總是有效的。轉換的合法性取決於源型別和目標型別。例如,不能直接將一個結構體型別轉換為整數或者浮點數。

type myStruct struct {
    field int
}

var a myStruct
// var b int = int(a)  // 這是不合法的

型別推斷

型別推斷是編譯器自動推斷變數型別的過程。在 Go 中,這通常發生在使用 := 進行變數初始化時。

定義

當使用 := 操作符宣告變數但沒有明確指定型別時,Go 編譯器會根據右側表示式的型別來推斷變數的型別。

z := 42  // 型別被推斷為 int

在複雜表示式中的型別推斷

在涉及多種型別的複雜表示式中,Go 會盡量進行型別推斷以滿足表示式的型別需要。

var m float64 = 3.14
n := int(m) + 42  // int(m) 顯式轉換,結果型別推斷為 int

型別推斷與型別不確定值

型別推斷也適用於型別不確定值。編譯器會根據上下文來推斷其最合適的型別。

const p = 5  // 型別不確定值
var q = p    // 型別推斷為 int,因為 p 的預設型別是 int

四、常數

在 Go 語言中,常數(Constant)是不可改變的值,這一點與變數有明顯區別。一旦一個常數被定義,它的值就不能再被改變。常數可以是型別不確定值或型別確定值。

型別不確定常數

型別不確定常數是沒有明確型別的常數。這些常數不是與任何具體的資料型別繫結的,因此在編譯時,Go 編譯器會根據上下文來推斷其型別。

const a = 42  // a 是型別不確定值,因為沒有明確指定型別

型別確定常數

型別確定常數是與特定資料型別明確繫結的常數。

const c int = 42  // c 是型別確定值,因為其型別明確為 int

常數宣告中的自動補全

在常數宣告塊中,你可以省略後續常數的型別和值,它們會自動補全。

const (
    x int = 10
    y      // y 也是 int 型別,值為 10
)

使用 iota 在常數宣告中

iota 是一個特殊的常數生成器,主要用於建立一組遞增的整數常數。

const (
    zero = iota  // zero 的值為 0
    one          // one 的值為 1
    two          // two 的值為 2
)

常數的可見性和可定址性

常數可以是匯出或非匯出的,取決於它的名稱是否以大寫字母開頭。常數不是可定址的。

const ExportedConst = "I am visible" // 可匯出的常數
const unExportedConst = "I am not visible" // 不可匯出的常數

常數溢位和預設型別

一個型別不確定常數所表示的值可能會溢位其預設型別。在這種情況下,只有當這個常數被用於一個可以容納該值的上下文時,編譯才會成功。

const big = 1 << 100 // 預設型別為 int,但值會溢位
var bigInt = big >> 99 // big 被用於一個 int64 上下文,沒有溢位

字串常數

字串常數是包含在雙引號中的一系列字元。

const hello = "Hello, world!"

布林常數

布林常數只有兩個可能的值:truefalse

const flagTrue = true
const flagFalse = false

列舉常數

通過使用 iota,你可以建立一組遞增的整數常數,通常用於列舉。

type Weekday int

const (
    Sunday Weekday = iota
    Monday
    Tuesday
    // ...
    Saturday
)

複數常數

Go 支援複數型別,並且你可以建立複數常數。

const complexConst = 1 + 2i

強制型別轉換

你可以通過強制型別轉換將一個常數轉換為另一種型別。

const integer = 42
const floatType = float32(integer)

計算表示式中的常數

常數也可以是計算表示式的結果,但表示式必須只包含常數值。

const calculated = 3 * 2  // 6

常數陣列和切片

需要注意的是,在 Go 中,陣列大小需要是常數。但切片和對映的大小可以動態改變,因此它們不能是常數。

const arraySize = 5
var arr [arraySize]int  // 合法

五、變數

變數是儲存資料值的儲存單元,其值可以在執行時改變。在 Go 語言中,變數的使用非常靈活但又具有嚴格的型別安全性。

變數宣告和賦值

基本宣告

使用 var 關鍵字進行變數宣告。

var x int

同時宣告和賦值

var y int = 42

或者使用短變數宣告形式:

z := 42

多變數宣告

var a, b, c int

或者

var (
    d int
    e float64
)

型別描述

基本型別

包括 int, float64, bool, string 等。

var integerVar int = 10
var floatVar float64 = 10.99
var boolVar bool = true
var stringVar string = "Hello, Go"

數值型別

Go 支援多種數值型別,包括 int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64 等。

var smallInt int8 = 127
var largeInt int64 = 9223372036854775807
var unsignedInt uint16 = 65535
var floatNum float32 = 3.141592

字元和字串

Go 有 runebyte 型別來表示 Unicode 字元和 ASCII 字元。

var asciiChar byte = 'A'
var unicodeChar rune = '你'
var str string = "Hello, World!"

布林型別

布林型別只有兩個值:truefalse

var isActive bool = true
var isCompleted bool = false

指標

Go 支援指標型別,但不支援指標運算。

var pointer *int
x := 42
pointer = &x

陣列和切片

var arrayExample [5]int = [5]int{1, 2, 3, 4, 5}
var sliceExample []int = arrayExample[1:4]

對映(Maps)

var countryCapitalMap map[string]string
countryCapitalMap = make(map[string]string)
countryCapitalMap["France"] = "Paris"

結構體(Structs)

type Employee struct {
    ID     int
    Name   string
    Salary float64
}

var emp Employee

介面(Interfaces)

type Shape interface {
    Area() float64
}

type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return 3.14159 * c.Radius * c.Radius
}

var s Shape = Circle{5.0}

函數型別

Go 中,函數也是一種型別,可以作為引數傳遞。

type AddFunc func(a int, b int) int

var add AddFunc = func(a int, b int) int {
    return a + b
}

通道(Channels)

var messageChannel chan string = make(chan string)

複合型別

例如陣列、切片、對映、通道、結構體和介面。

陣列

var arr [5]int

切片

var slice []int

對映

var mapping map[string]int

通道

var ch chan int

結構體

type Person struct {
    name string
    age int
}

var p Person

介面

type Geometry interface {
    Area() float64
}

var g Geometry

值的可定址性

Go 語言中有些變數是可定址的,意味著你可以獲取這個變數的記憶體地址。

var myVar int = 42
var p *int = &myVar

變數作用域

在 Go 中,變數可以有不同的作用域:全域性作用域、包內作用域、函數作用域、程式碼塊作用域等。

var globalVar int  // 全域性作用域

func myFunction() {
    var functionVar int  // 函數作用域
    {
        var blockVar int  // 程式碼塊作用域
    }
}

七、常數和變數的高階用法

在 Go 程式設計中,常數和變數不僅僅是資料儲存的簡單方式。它們有各種高階用法,可以用來優化程式碼、提高效率或實現複雜的程式設計模式。

常數的高階用法

列舉和 iota

Go 通過 iota 關鍵字支援列舉型別的實現。這在一組常數宣告中是非常有用的。

const (
    Monday = iota + 1  // Monday = 1
    Tuesday            // Tuesday = 2
    Wednesday          // Wednesday = 3
)

型別別名

使用型別別名,你可以將常數限制為自定義型別。

type Weekday int

const (
    Sunday Weekday = iota
    Monday
    // ...
)

無型別和有型別常數

無型別的常數可以用在多種場合,它們會根據上下文進行型別推斷。

const x = 42  // 無型別常數
var i int = x
var f float64 = x

變數的高階用法

變數的作用域

Go 支援塊級作用域。通過 var 關鍵字在不同級別(全域性、包級別、函數級別等)宣告變數,你可以控制變數的可見性。

var globalVar = 42  // 全域性變數

func main() {
    var funcVar = "I'm local"  // 函數級別變數
}

延遲初始化

使用 init() 函數,你可以在程式開始執行前初始化變數。

var complexVar complex128

func init() {
    complexVar = cmplx.Sqrt(-5 + 12i)
}

指標和地址操作符

雖然 Go 語言沒有提供指標運算,但它允許你通過地址操作符 & 和解除參照操作符 * 來操作指標。

x := 42
p := &x
fmt.Println(*p)  // 輸出 42

使用標籤(Tags)和結構體

在結構體中,你可以使用標籤(tags)來附加後設資料,這在序列化和反序列化時非常有用。

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

關注公眾號【TechLeadCloud】,分享網際網路架構、雲服務技術的全維度知識。作者擁有10+年網際網路服務架構、AI產品研發經驗、團隊管理經驗,同濟本復旦碩,復旦機器人智慧實驗室成員,阿里雲認證的資深架構師,專案管理專業人士,上億營收AI產品研發負責人。

如有幫助,請多關注
個人微信公眾號:【TechLeadCloud】分享AI與雲服務研發的全維度知識,談談我作為TechLead對技術的獨特洞察。
TeahLead KrisChang,10+年的網際網路和人工智慧從業經驗,10年+技術和業務團隊管理經驗,同濟軟體工程本科,復旦工程管理碩士,阿里雲認證雲服務資深架構師,上億營收AI產品業務負責人。