golang 中的四種型別轉換總結

1112發表於2020-04-03

go存在4種型別轉換分別為:斷言、強制、顯式、隱式。

通常說的型別轉換是指斷言,強制在日常不會使用到、顯示是基本的型別轉換、隱式使用到但是不會注意到。斷言、強制、顯式三類在go語法描述中均有說明,隱式是在日常使用過程中總結出來。

斷言通過判斷變數是否可以轉換成某一個型別

型別斷言

Type assertions語法文件 映象地址

一個簡單的斷言表示式:

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的斷言方法。

Type switches語法文件 映象地址

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介面就沒事,如果沒有實現在編譯時期就會報錯,實現編譯期間檢測介面是否實現。

Conversions語法文件 映象地址

一個顯式轉換的表示式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 協議》,轉載必須註明作者和本文連結

相關文章