go存在4種型別轉換分別為:斷言、強制、顯式、隱式。
通常說的型別轉換是指斷言,強制在日常不會使用到、顯示是基本的型別轉換、隱式使用到但是不會注意到。斷言、強制、顯式三類在go語法描述中均有說明,隱式是在日常使用過程中總結出來。
斷言通過判斷變數是否可以轉換成某一個型別
型別斷言
一個簡單的斷言表示式:
var s = x.(T)
如果x不是nil,且x可以轉換成T型別,就會斷言成功,返回T型別的變數s。如果T不是介面型別,則要求x的型別就是T,如果T是一個介面,要求x實現了T介面。
如果斷言型別成立,則表示式返回值就是T型別的x,如果斷言失敗就會觸發panic。
上述表所示再斷言失敗就會panic,go提供了另外一種帶返回是否成立的斷言語法:
s, ok := x.(T)
該方法和第一種差不多一樣,但是ok會返回是否斷言成功不會出現panic,ok就表示是否是成功了。
型別switch
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
本文件僅大概說明一下,具體研究請求查詢相關資料。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型別:
- x可以分配成T型別。
- 忽略struct標籤x的型別和T具有相同的基礎型別。
- 忽略struct標記x的型別和T是未定義型別的指標型別,並且它們的指標基型別具有相同的基礎型別。
- x的型別和T都是整數或浮點型別。
- x的型別和T都是複數型別。
- x的型別是整數或[]byte或[]rune,並且T是字串型別。
- x的型別是字串,T型別是[]byte或[]rune。
例如下列程式碼利用了規則進行轉換,規則實現可以參考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
本作品採用《CC 協議》,轉載必須註明作者和本文連結