淺談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{} = 1var s uint8 = 1
temp1 := int(s)
temp2 := v.(int)
fmt.Println(temp1,temp2)
Go的型別系統瞭解
Go的型別
Go語言是一門靜態編譯型語言,是一門強型別語言,Go語言中型別分為兩種:命名型別(已定義型別)和未命名型別(組合型別),我舉例說一下
- 命名型別(已定義型別)
uint8(byte) uint16 uint32 uint64 int int8 int16 int32(rune) int64 bool stringfloat32 float64 complex64 complex128
上面舉例型別歸為三大類:,數值型別,字串型別, 布林值型別,我們使用type定義的任何型別也被稱為命名型別,如下
//也是命名型別type MyBool bool
- 未命名型別 (組合型別)
slice map chan function interface struct pointer
上面舉例的型別有容器型別,函式型別,指標型別,結構體型別,通道型別,介面型別
自定義型別和底層型別
Go允許通過type關鍵字定義一個型別
Go的每一個型別都一個底層型別,型別的底層型別有如下規律
- 每一個命名型別的底層型別都是自己
- 每一個組合型別的底層型別都是自己
- 在一個型別的宣告中,新宣告的型別和原型別的底層型別是共享的
如下程式碼,請問這段程式碼能夠編譯成功嗎?為什麼?首先這段程式碼是編譯失敗的,i的型別是MyInt,j的型別是int,雖說她們的底層型別都是int,但不能相互賦值,也就說明命名型別間是不能相互賦值的,即便是低限制往高限制賦值,比如 int32 賦給 int64也是編譯失敗的
type MyInt intfunc 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 inttype I MyInt
type Ints []inttype MyInts []MyInt
type M map[string]stringtype 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)
//這個轉換型別不同,但相容,所以OKvar j int8 = 1
m := int(j)
//這個轉換是失敗的,系統會檢測到型別不匹配,直接panicvar 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
//這個轉換沒有任何問題,都OKvar i int8 = 123var j int16 = int16(i)
//這個轉換會丟失精度,從高位轉低位var m int64 = 128var 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))
}
微信搜尋公眾號 "技術人技術事",閱讀更多精彩文章
相關文章
- 淺談 Go 型別轉換之間的那些事Go型別
- 淺談JavaScript的型別轉換JavaScript型別
- Goland 時間轉換的那些事GoLand
- Java 資料型別之間的轉換Java資料型別
- 字串和Date型別之間的轉換字串型別
- go interface{}型別轉換Go型別
- Map和String型別之間的轉換型別
- date和timestamp型別之間的轉換型別
- java基本型別和物件之間的轉換Java型別物件
- 淺談Blazor開發的那些事Blazor
- go-常用型別轉換Go型別
- 5.JavaScript資料型別之間的轉換JavaScript資料型別
- NodeJS 和 C++ 之間的型別轉換NodeJSC++型別
- 談談 MySQL 隱式型別轉換MySql型別
- 字元型別轉換成時間型別字元型別
- 淺談C#中的資料型別轉換與物件複製C#資料型別物件
- Go interface 原理剖析--型別轉換Go型別
- MySQL型別轉換注意事項MySql型別
- Java long型別和Long型別的那些事Java型別
- 再談c++型別轉換C++型別
- JavaScript的隱式型別轉換淺析JavaScript型別
- 淺談Oracle中隱式型別轉換規律和影響Oracle型別
- Go小工具系列——型別轉換Go型別
- go語言資料型別轉換Go資料型別
- 7.GoLang中基本資料型別之間的轉換Golang資料型別
- 時間型別及格式轉換型別
- 強制型別轉換之(==)型別
- js資料型別間的互相轉換JS資料型別
- Java學習--Java 中基本型別和字串之間的轉換Java型別字串
- 深入淺出說強制型別轉換型別
- 淺談querySelector和getElementById之間的區別
- Java--包裝類(基本型別和字串之間的轉換)、進位制轉換Java型別字串
- Session、Cookie、Token 【淺談三者之間的那點事】SessionCookie
- 淺談AsyncLocal,我們應該知道的那些事兒
- java- 型別-轉換:基本型別以及包裝型別的轉換Java型別
- PHP 型別轉換&&型別強制轉換PHP型別
- 【C++注意事項】1 資料型別及型別轉換C++資料型別
- 【Go】IP地址轉換:數字與字串之間高效轉換Go字串