go存在4種類型轉換分別爲:斷言、強制、顯式、隱式。golang
一般說的類型轉換是指斷言,強制在平常不會使用到、顯示是基本的類型轉換、隱式使用到可是不會注意到。斷言、強制、顯式三類在go語法描述中均有說明,隱式是在平常使用過程當中總結出來。c#
斷言經過判斷變量是否能夠轉換成某一個類型函數
一個簡單的斷言表達式:測試
var s = x.(T)
ui
若是x不是nil,且x能夠轉換成T類型,就會斷言成功,返回T類型的變量s。若是T不是接口類型,則要求x的類型就是T,若是T是一個接口,要求x實現了T接口。google
若是斷言類型成立,則表達式返回值就是T類型的x,若是斷言失敗就會觸發panic。指針
上述表所示再斷言失敗就會panic,go提供了另一種帶返回是否成立的斷言語法:code
s, ok := x.(T)
接口
該方法和第一種差很少同樣,可是ok會返回是否斷言成功不會出現panic,ok就表示是不是成功了。
go語法種還提供了另一種類型switch的斷言方法。
x斷言成了type類型,type類型具體值就是switch case的值,若是x成功斷言成了某個case類型,就能夠執行那個case,此時i := x.(type)
返回的i就是那個類型的變量了,能夠直接看成case類型使用。
switch i := x.(type) { case nil: printString("x is nil") // type of i is type of x (interface{}) case int: printInt(i) // type of i is int case float64: printFloat64(i) // type of i is float64 case func(int) float64: printFunction(i) // type of i is func(int) float64 case bool, string: printString("type is bool or string") // type of i is type of x (interface{}) default: printString("don't know the type") // type of i is type of x (interface{}) }
強制類型轉換經過修改變量類型
該方法不常見,主要用於unsafe包和接口類型檢測,須要懂得go變量的知識。
本文檔僅大概說明一下,具體研究請求查找相關資料。unsafe語法文檔 鏡像地址
var f float64 bits = *(*uint64)(unsafe.Pointer(&f)) type ptr unsafe.Pointer bits = *(*uint64)(ptr(&f)) var p ptr = nil
float64就強制轉換成uint64類型,float的地址就是一個值可是類型是float64,而後建立了一個uint64類型變量,地址值也是float64的地址值,兩個變量值相同類型不一樣,強制轉換了類型。
unsafe強制轉換是指針的底層操做了,用c的朋友就很熟悉這樣的指針類型轉換,利用內存對齊才能保證轉換可靠,例如int和uint存在符號位差異,利用unsafe轉換後值可能不一樣,可是在內存存儲二進制如出一轍。
例以下列代碼:
var _ Context = (*ContextBase)(nil)
nil的類型是nil地址值爲0,利用強制類型轉換成了*ContextBase,返回的變量就是類型爲*ContextBase地址值爲0,而後Context=xx
賦值若是xx實現了Context接口就沒事,若是沒有實如今編譯時期就會報錯,實現編譯期間檢測接口是否實現。
一個顯式轉換的表達式T(x) ,其中T是一種類型而且x是可轉換爲類型的表達式T,例如:uint(666)
。
在如下任何一種狀況下,變量x均可以轉換成T類型:
例以下列代碼利用了規則進行轉換,規則實現能夠參考reflect.Value.Convert方法邏輯:
int64(222) []byte("ssss") type A int A(2)
隱式類型轉換平常使用並不會感受到,可是運行中確實出現了類型轉換,如下列出了兩種。
type Reader interface { Read(p []byte) (n int, err error) } type ReadCloser interface { Reader Close() error } var rc ReaderClose r := rc
ReaderClose接口組合了Reader接口,可是r=rc的賦值時仍是類型轉換了,go使用系統內置的函數執行了類型轉換。之前遇到過相似接口組合類型的變量賦值,而後使用pprof和bench測試發現了這一細節,在接口類型轉移時浪費了一些性能。
type Handler func() func NewHandler() Handler { return func() {} }
雖然type定義了Handler類型,可是Handler和func()是兩種實際類型,類型不會相等,使用反射和斷言均會出現兩種類型不一樣。
二者類型不一樣驗證代碼:
package main import ( "fmt" "reflect" ) type Handler func() func a() Handler { return func() {} } func main() { var i interface{} = main _, ok := i.(func()) fmt.Println(ok) _, ok = i.(Handler) fmt.Println(ok) fmt.Println(reflect.TypeOf(main) == reflect.TypeOf((*Handler)(nil)).Elem()) } // true // false // false