Go 語言類型系統詳解

這是『就要學習 Go 語言』系列的第 18 篇分享文章編程

什麼是類型

不一樣的編程語言之間,類型的概念有所不一樣,能夠用許多不一樣的方式來表達,但大致上都有一些相同的地方。編程語言

  1. 類型是一組值;
  2. 相同類型的值之間能夠進行哪些操做,例如:int 類型能夠執行 + 和 - 等運算,而對於字符類型,能夠執行鏈接、空檢查等操做;

所以,語言類型系統指定哪些運算符對哪些類型有效。學習

Go 語言的類型系統

boolean、numeric 和 string 是 Go 的基礎數據類型,也稱爲預聲明類型(pre-declared type),可用來構造其餘的類型,例如字面量類型。ui

字面量類型 (type literal):由預聲明類型組合而成(沒有用 type 關鍵字定義),例如:[3]int 、chan int、map[string] string、* int 等。spa

由字面量類型可構成複合類型,如:array、struct、map、slice、channel、func、interface 等。.net

命名類型和未命名類型

具備名稱的類型:例如 int、int6四、float3二、string、bool 等預先聲明類型。另外,使用 type 關鍵字聲明的任意類型也稱爲命名類型。翻譯

var i int // named type
type myInt int // named type
var b bool // named type
複製代碼

未命名類型:上面提到的複合類型,包括 array、struct、pointer、function、interface、slice、Map 和 channel,都是未命名類型。它們沒有名稱,可是有關於如何組成的字面量描述符。指針

[]string // unnamed type
map[string]string // unnamed type
[10]int // unnamed type
複製代碼

底層類型

每種類型都有底層類型,若是 T 是預聲明類型或字面量類型,則底層類型就是 T 自己;不然,T 的底層類型是 T 在定義時引用的類型的底層類型。code

type A string      // string
type B A			// string
type M map[string]int // map[string]int
type N M			// map[string]int
type P *N			// *N
type S string		// string
type T map[S]int	// map[S]int
type U T 			// map[S]int
複製代碼

第 一、 6 行,預聲明的字符串類型,所以底層類型是 T 自己,即字符串;cdn

第 3 、5 行,是字面量類型,所以底層類型就是 T 自己,即 map[string]int 和 指針 *N。注意:字面量類型也是未命名類型;

第 2 、四、8 行,T 的底層類型是 T 在其定義時引用的類型的底層類型,例如:B 引用了 A,因此 B 的底層類型是字符串類型,其餘狀況同理;

咱們再來看下第 7 行的例子:type T map[S]int ,因爲 S 的底層類型是 string,難道此時 T 的底層類型不該該是 map[string]int 而不是 map[S]int 嗎?由於咱們在談論 map[S]int 的底層未命名類型,因此向下追溯到未命名類型,正如 Go 語言規範上寫的同樣:若是 T 是字面量類型,則對應的底層類型就是 T 自己。

可賦值性

關於變量的可賦值性在 Go 語言的文檔中已經講得很清楚了,咱們來看其中比較重要的一條:當變量 a 能夠賦值給類型 T 的變量時,二者都應該具備相同的底層類型,而且至少其中一個不是命名類型

看下代碼

package main

type aInt int

func main() {
    var i int = 10
    var ai aInt = 100
    i = ai
    printAiType(i)
}

func printAiType(ai aInt) {
    print(ai)
}
複製代碼

上面的代碼編譯不經過,編譯時報錯:

8:4: cannot use ai (type aInt) as type int in assignment
9:13: cannot use i (type int) as type aInt in argument to printAiType
複製代碼

由於 i 是命名類型 int,而 ai 是命名類型 aInt,雖然它們的底層類型相同,都是 int。

package main

type MyMap map[int]int

func main() {
    m := make(map[int]int)
    var mMap MyMap
    mMap = m
    printMyMapType(mMap)
    print(m)
}

func printMyMapType(mMap MyMap) {
    print(mMap)
}
複製代碼

上面這段代碼編譯經過,由於 m 是未命名類型而且 m 和 mMap 的底層類型相同。

類型轉化

看下類型轉化的規範

類型轉化規範

package main

type Meter int64

type Centimeter int32

func main() {
    var cm Centimeter = 1000
    var m Meter
    m = Meter(cm)
    print(m)
    cm = Centimeter(m)
    print(cm)
}
複製代碼

上面的代碼能夠編譯經過,由於 MeterCentimeter 都是整型,而且它們的底層類型能夠相互轉化。

類型一致性

兩種類型要麼相同要麼不一樣。

已定義類型與其餘任意類型老是不一樣。所以,即便預先聲明的命名類型 int、int64 等也是不相同的。

來看下結構體的一條轉化規則:

x 賦值給 T 時,不考慮結構體標籤,x 和 T 應具備相同的底層類型

package main

type Meter struct {
    value int64
}

type Centimeter struct {
    value int32
}

func main() {
    cm := Centimeter{
        value: 1000,
    }

    var m Meter
    m = Meter(cm)
    print(m.value)
    cm = Centimeter(m)
    print(cm.value)
}
複製代碼

記住一點:相同的底層類型。因爲成員 Meter.value 的底層類型是 int64,而成員 Centimeter.value 的底層類型是 int32,因此它們不相同,由於已定義類型與其餘任意類型老是不一樣。因此上面的代碼片斷編譯會出錯。

package main

type Meter struct {
    value int64
}

type Centimeter struct {
    value int64
}

func main() {
    cm := Centimeter{
        value: 1000,
    }

    var m Meter
    m = Meter(cm)
    print(m.value)
    cm = Centimeter(m)
    print(cm.value)
}
複製代碼

成員 Meter.value 和 Centimeter.value 的底層類型都是 int64,因此它們相同,編譯能夠經過。

ps:我在 GCTT(Go 中國翻譯組) 翻譯的第一篇文章就是關於 Go 語言的類型系統的,今天整理處理給你們看下,但願這篇文章對你理解 Go 類型系統有所幫助!


(全文完)

原創文章,若需轉載請註明出處!
歡迎掃碼關注公衆號「Golang來啦」或者移步 seekload.net ,查看更多精彩文章。

公衆號「Golang來啦」給你準備了一份神祕學習大禮包,後臺回覆【電子書】領取!

公衆號二維碼
相關文章
相關標籤/搜索