天天看點

淺談Go類型轉換之間的那些事

淺談Go類型轉換之間的那些事

試着答一答這些問題

s[i]和(for _,v range)的v的差別是什麼
var s string = "AB"
fmt.Println(reflect.TypeOf(s[0]))
for _, v := range s {
   fmt.Println(reflect.TypeOf(v))
}           
a.(),和 a(b) 的差別是什麼?
var v interface{} = 1
var s uint8 = 1

temp1 := int(s)
temp2 := v.(int)

fmt.Println(temp1,temp2)           

Go的類型系統了解

Go的類型

Go語言是一門靜态編譯型語言,是一門強類型語言,Go語言中類型分為兩種:命名類型(已定義類型)和未命名類型(組合類型),我舉例說一下

  1. 命名類型(已定義類型)
uint8(byte) uint16 uint32 uint64 int int8 int16 int32(rune) int64 bool string
float32 float64 complex64 complex128           

上面舉例類型歸為三大類:,數值類型,字元串類型, 布爾值類型,我們使用type定義的任何類型也被稱為命名類型,如下

//也是命名類型
type MyBool bool            
  1. 未命名類型 (組合類型)
slice map chan function interface struct pointer           

上面舉例的類型有容器類型,函數類型,指針類型,結構體類型,通道類型,接口類型

自定義類型和底層類型

Go允許通過type關鍵字定義一個類型

Go的每一個類型都一個底層類型,類型的底層類型有如下規律

  1. 每一個命名類型的底層類型都是自己
  2. 每一個組合類型的底層類型都是自己
  3. 在一個類型的聲明中,新聲明的類型和原類型的底層類型是共享的

如下代碼,請問這段代碼能夠編譯成功嗎?為什麼?首先這段代碼是編譯失敗的,i的類型是MyInt,j的類型是int,雖說她們的底層類型都是int,但不能互相指派,也就說明命名類型間是不能互相指派的,即便是低限制往高限制指派,比如 int32 賦給 int64也是編譯失敗的

type MyInt int
func CustomType() {
   var i MyInt = 2
   var j int = 1
   j = i
   i = j
   fmt.Println(i == j)
}           

下面這段代碼會列印這兩個變量的基本類型和底層類型,

//輸出MyInt int
fmt.Println(reflect.TypeOf(i), reflect.TypeOf(j))
//輸出int int
fmt.Println(reflect.TypeOf(i).Kind(), reflect.TypeOf(j).Kind())           

我們再來看一個Demo,下面這段代碼編譯會報錯嗎,如果把int32改成int64呢?答案是編譯報錯,改成int64也會編譯報錯,隻有j和int32同時改成i和int64,才會編譯成功。因為這時m和n的底層類型是完全相同的。

type MyM struct {
   i int64
}
type MyN struct {
   j int32
}
func TestStruct() {
   n := MyN{j: 10}
   var m MyM
   m = MyM(n)
  fmt.Println(n,m)
}           
如何追蹤朔源一個類型的的底層類型

如下代碼,說說這些類型的底層類型是什麼?

type MyInt int
type I MyInt
type Ints []int
type MyInts []MyInt
type M map[string]string
type CustomM M           

MyInt的底層類型是int

I的底層類型時int

Ints的底層類型是[]int

MyInts的底層類型是slice

M的底層類是map

CustomM的底層類是map

規律就是直到找到的一個内置類型(Go内置的類型)或者未命名類型(組合類型)結束,這個類型就是目前類型的底層類型

怎麼通過代碼擷取一個類型的底層類型呢?通過下面代碼擷取

reflect.TypeOf(variable).Kind()           
類型别名

什麼是類型别名呢?Go中有兩個類型别名 byte,對應的真實類型是uint8,rune,對應的真實類型是int32,我們可以源代碼中這兩個的定義如下

// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is
// used, by convention, to distinguish byte values from 8-bit unsigned
// integer values.
type byte = uint8

// rune is an alias for int32 and is equivalent to int32 in all ways. It is
// used, by convention, to distinguish character values from integer values.
type rune = int32           

從這個就能就能解決最開始的第一個問題,s[index]取得是字元串轉換成位元組後的某一個位元組,而range指的是循環字元串s的每一個字元(range會隐式的unicode解碼), 但字元區分字母和漢字,一個字母占用一個位元組,一個漢字可不是了,看如下代碼,你可以擷取byte和rune的底層類型

var r rune = 'c'
var b byte = 1
fmt.Println(reflect.TypeOf(r).Kind()) //int32
fmt.Println(reflect.TypeOf(b).Kind()) //uint8           

如何定義一個類型别名呢?其實很簡單,知道怎麼定義一個類型,那麼定義一個類型别名就很簡單了,參考上面的byte和rune,如下我們為int64定義一個别名(從Go1.9開始支援),類型别名是可以被聲明在函數體内的

//相比定義一個類型多了一個=号
type alaisInt64 = int64           
類型轉換和斷言

類型轉換是用來在類型不同但互相相容的類型之間的互相轉換的方式,如果不相容,則無法互相轉換,編譯會報錯,通常寫法是 a(b),把b轉換成a

類型斷言是在接口之間進行,本質也是類型轉換,寫法是a.(b),含義是把a轉換成b

如下代碼,做一些錯誤的和正确的示範

//這個轉換時類型不同,也不相容,是以編譯報錯
s := "ab"
i := int(s)

//這個轉換類型不同,但相容,是以OK
var j int8 = 1
m := int(j)

//這個轉換是失敗的,系統會檢測到類型不比對,直接panic
var k interface{} = "s"
l := k.(int)
//但我們可以通過一個參數來判斷,隻有f為true時,才會轉換成功
l,f := k.(int)
//這個轉換是成功的
p,f := k.(string)           

類型轉換的實踐,勤加練習才能了解

數字類型之間轉換

從低位轉高位沒有什麼問題,從高位轉低位時(會丢失精度),int64轉int8,這個轉換的過程如下:

128的二進制:.........00000000_10000000

因為是從int64轉int8,是以截取128的後八位 :10000000

此時最高位是1,表示這是一個負數,此時結果是就是:-128

//這個轉換沒有任何問題,都OK
var i int8 = 123
var j int16 = int16(i)
//這個轉換會丢失精度,從高位轉低位
var m int64 = 128
var n int8 = int8(m) //n的結果是-128,因為int8能表達的最大值是127,最小值是-128,           
字元串,位元組,數字,字元互相轉換
var s1,s2 string = "AbcD","1234"
//轉位元組
bs1 := []byte(s1); bs2 := []byte(s2)

//位元組數組轉字元串
s11 := string(bs1); s22 := string(bs2)
//單個位元組轉字元串
ss := string(bs1[0])
fmt.Println(s11, s22, ss)

//s2轉數字 ,err 表示是否能轉換成功,比如s1就會轉換失敗
i, err := strconv.Atoi(s2)
//數字轉字元串
s := strconv.Itoa(i)

//字元串轉字元數組
runes := []rune(s1)

//字元數組轉字元串
ss1 := string(runes)
//單個字元轉字元串
ss2 := strconv.QuoteRune(runes[0])

//字元轉位元組
bss := make([]byte, 0)
bss = strconv.AppendQuoteRune(bss, runes[0])
fmt.Println(err, s, ss1, ss2, runes[0], bss, string(bss))
//除開rune和byte底層的類型的差別,在使用上,
//rune能處理一切的字元,而byte僅僅局限在ascii

//整形轉位元組
x := int32(68)
bytesBuffer := bytes.NewBuffer([]byte{})
binary.Write(bytesBuffer, binary.BigEndian, x)
//位元組轉整形
var y int32
binary.Read(bytesBuffer, binary.BigEndian, &y)           
接口到具體類型的轉換
//由接口類型轉換為具體的類型
var i interface{} = 1
t, f := i.(int)
if f { //轉換成功
   fmt.Println(t)
} else {//轉換失敗
   fmt.Println(reflect.TypeOf(i).Kind(), reflect.TypeOf(i))
}           

歡迎關注公衆号,閱讀更多精彩文章

posted on

2020-01-09 13:36 

阿偉~ 

閱讀(...) 

評論(...) 

編輯 

收藏