日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

golang: 类型转换和类型断言

發布時間:2025/3/20 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 golang: 类型转换和类型断言 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

類型轉換在程序設計中都是不可避免的問題。當然有一些語言將這個過程給模糊了,大多數時候開發者并不需要去關注這方面的問題。但是golang中的類型匹配是很嚴格的,不同的類型之間通常需要手動轉換,編譯器不會代你去做這個事。我之所以說通常需要手動轉換,是因為interface類型作為一個特例,會有不同的處理方式。

golang中的所有類型都有自己的默認值,對此我做了個測試。

$GOPATH/src

----typeassert_test

--------main.go

main.go的代碼如下:

package?mainimport?("fmt")type?myStruct?struct?{name???booluserid?int64 }var?structZero?myStructvar?intZero?intvar?int32Zero?int32var?int64Zero?int64var?uintZero?uintvar?uint8Zero?uint8var?uint32Zero?uint32var?uint64Zero?uint64var?byteZero?bytevar?boolZero?boolvar?float32Zero?float32var?float64Zero?float64var?stringZero?stringvar?funcZero?func(int)?intvar?byteArrayZero?[5]bytevar?boolArrayZero?[5]boolvar?byteSliceZero?[]bytevar?boolSliceZero?[]boolvar?mapZero?map[string]boolvar?interfaceZero?interface{}var?chanZero?chan?intvar?pointerZero?*intfunc?main()?{fmt.Println("structZero:?",?structZero)fmt.Println("intZero:?",?intZero)fmt.Println("int32Zero:?",?int32Zero)fmt.Println("int64Zero:?",?int64Zero)fmt.Println("uintZero:?",?uintZero)fmt.Println("uint8Zero:?",?uint8Zero)fmt.Println("uint32Zero:?",?uint32Zero)fmt.Println("uint64Zero:?",?uint64Zero)fmt.Println("byteZero:?",?byteZero)fmt.Println("boolZero:?",?boolZero)fmt.Println("float32Zero:?",?float32Zero)fmt.Println("float64Zero:?",?float64Zero)fmt.Println("stringZero:?",?stringZero)fmt.Println("funcZero:?",?funcZero)fmt.Println("funcZero?==?nil?",?funcZero?==?nil)fmt.Println("byteArrayZero:?",?byteArrayZero)fmt.Println("boolArrayZero:?",?boolArrayZero)fmt.Println("byteSliceZero:?",?byteSliceZero)fmt.Println("byteSliceZero's?len?",?len(byteSliceZero))fmt.Println("byteSliceZero's?cap?",?cap(byteSliceZero))fmt.Println("byteSliceZero?==?nil?",?byteSliceZero?==?nil)fmt.Println("boolSliceZero:?",?boolSliceZero)fmt.Println("mapZero:?",?mapZero)fmt.Println("mapZero's?len?",?len(mapZero))fmt.Println("mapZero?==?nil?",?mapZero?==?nil)fmt.Println("interfaceZero:?",?interfaceZero)fmt.Println("interfaceZero?==?nil?",?interfaceZero?==?nil)fmt.Println("chanZero:?",?chanZero)fmt.Println("chanZero?==?nil?",?chanZero?==?nil)fmt.Println("pointerZero:?",?pointerZero)fmt.Println("pointerZero?==?nil?",?pointerZero?==?nil) }


$?cd?$GOPATH/src/typeassert_test$?go?build$?./typeassert_test


您可以清楚的了解到各種類型的默認值。如bool的默認值是false,string的默認值是空串,byte的默認值是0,數組的默認就是這個數組成員類型的默認值所組成的數組等等。然而您或許會發現在上面的例子中:map、interface、pointer、slice、func、chan的默認值和nil是相等的。關于nil可以和什么樣的類型做相等比較,您只需要知道nil可以賦值給哪些類型變量,那么就可以和哪些類型變量做相等比較。官方對此有明確的說明:http://pkg.golang.org/pkg/builtin/#Type,也可以看我的另一篇文章:golang: 詳解interface和nil。所以現在您應該知道nil只能賦值給指針、channel、func、interface、map或slice類型的變量。如果您用int類型的變量跟nil做相等比較,panic會找上您。

對于字面量的值,編譯器會有一個隱式轉換。看下面的例子:

package?mainimport?( "fmt")func?main()?{ var?myInt?int32?????=?5var?myFloat?float64?=?0fmt.Println(myInt)fmt.Println(myFloat) }


對于myInt變量,它存儲的就是int32類型的5;對于myFloat變量,它存儲的是int64類型的0。或許您可能會寫出這樣的代碼,但確實不是必須這么做的:

package?mainimport?( "fmt")func?main()?{ var?myInt?int32?????=?int32(5) var?myFloat?float64?=?float64(0)fmt.Println(myInt)fmt.Println(myFloat) }


在C中,大多數類型轉換都是可以隱式進行的,比如:

#include?<stdio.h>int?main(int?argc,?char?**argv){int?uid??=?12345;long?gid?=?uid;printf("uid=%d,?gid=%d\n",?uid,?gid);return?0; }


但是在golang中,您不能這么做。有個類似的例子:

package?mainimport?( "fmt")func?main()?{ var?uid?int32?=?12345var?gid?int64?=?int64(uid)fmt.Printf("uid=%d,?gid=%d\n",?uid,?gid) }


很顯然,將uid賦值給gid之前,需要將uid強制轉換成int64類型,否則會panic。golang中的類型區分靜態類型和底層類型。您可以用type關鍵字定義自己的類型,這樣做的好處是可以語義化自己的代碼,方便理解和閱讀。

package?mainimport?( "fmt")type?MyInt32?int32func?main()?{ var?uid?int32???=?12345var?gid?MyInt32?=?MyInt32(uid)fmt.Printf("uid=%d,?gid=%d\n",?uid,?gid) }


在上面的代碼中,定義了一個新的類型MyInt32。對于類型MyInt32來說,MyInt32是它的靜態類型,int32是它的底層類型。即使兩個類型的底層類型相同,在相互賦值時還是需要強制類型轉換的。可以用reflect包中的Kind方法來獲取相應類型的底層類型。

對于類型轉換的截斷問題,為了問題的簡單化,這里只考慮具有相同底層類型之間的類型轉換。小類型(這里指存儲空間)向大類型轉換時,通常都是安全的。下面是一個大類型向小類型轉換的示例:

package?mainimport?( "fmt")func?main()?{ var?gid?int32?=?0x12345678var?uid?int8??=?int8(gid)fmt.Printf("uid=%#x,?gid=%#x\n",?uid,?gid) }


在上面的代碼中,gid為int32類型,也即占4個字節空間(在內存中占有4個存儲單元),因此這4個存儲單元的值分別是:0x12, 0x34, 0x56, 0x78。但事實不總是如此,這跟cpu架構有關。在內存中的存儲方式分為兩種:大端序和小端序。大端序的存儲方式是高位字節存儲在低地址上;小端序的存儲方式是高位字節存儲在高地址上。本人的機器是按小端序來存儲的,所以gid在我的內存上的存儲序列是這樣的:0x78, 0x56, 0x34, 0x12。如果您的機器是按大端序來存儲,則gid的存儲序列剛好反過來:0x12, 0x34, 0x56, 0x78。對于強制轉換后的uid,肯定是產生了截斷行為。因為uid只占1個字節,轉換后的結果必然會丟棄掉多余的3個字節。截斷的規則是:保留低地址上的數據,丟棄多余的高地址上的數據。來看下測試結果:

$?cd?$GOPATH/src/typeassert_test$?go?build$?./typeassert_testuid=0x78,?gid=0x12345678


如果您的輸出結果是:

uid=0x12,?gid=0x12345678


那么請不要驚訝,因為您的機器是屬于大端序存儲。

其實很容易根據上面所說的知識來判斷是屬于大端序或小端序:

package?mainimport?("fmt")func?IsBigEndian()?bool?{ var?i?int32?=?0x12345678var?b?byte??=?byte(i) if?b?==?0x12?{ return?true} return?false}func?main()?{ if?IsBigEndian()?{fmt.Println("大端序")}?else?{fmt.Println("小端序")} }


$?cd?$GOPATH/src/typeassert_test$?go?build$?./typeassert_test小端序


接口的轉換遵循以下規則:

  • 普通類型向接口類型的轉換是隱式的。

  • 接口類型向普通類型轉換需要類型斷言。

  • 普通類型向接口類型轉換的例子隨處可見,例如:

    package?mainimport?( "fmt")func?main()?{var?val?interface{}?=?"hello"fmt.Println(val)val?=?[]byte{'a',?'b',?'c'}fmt.Println(val) }


    正如您所預料的,"hello"作為string類型存儲在interface{}類型的變量val中,[]byte{'a', 'b', 'c'}作為slice存儲在interface{}類型的變量val中。這個過程是隱式的,是編譯期確定的。

    接口類型向普通類型轉換有兩種方式:Comma-ok斷言和switch測試。任何實現了接口I的類型都可以賦值給這個接口類型變量。由于interface{}包含了0個方法,所以任何類型都實現了interface{}接口,這就是為什么可以將任意類型值賦值給interface{}類型的變量,包括nil。還有一個要注意的就是接口的實現問題,*T包含了定義在T和*T上的所有方法,而T只包含定義在T上的方法。我們來看一個例子:

    package?mainimport?("fmt")//?演講者接口type?Speaker?interface?{ //?說Say(string) //?聽Listen(string)?string//?打斷、插嘴Interrupt(string) }//?王蘭講師type?WangLan?struct?{msg?string}func?(this?*WangLan)?Say(msg?string)?{fmt.Printf("王蘭說:%s\n",?msg) }func?(this?*WangLan)?Listen(msg?string)?string?{ this.msg?=?msg return?msg }func?(this?*WangLan)?Interrupt(msg?string)?{ this.Say(msg) }//?江婁講師type?JiangLou?struct?{msg?string}func?(this?*JiangLou)?Say(msg?string)?{fmt.Printf("江婁說:%s\n",?msg) }func?(this?*JiangLou)?Listen(msg?string)?string?{ this.msg?=?msg return?msg }func?(this?*JiangLou)?Interrupt(msg?string)?{ this.Say(msg) }func?main()?{wl?:=?&WangLan{}jl?:=?&JiangLou{} var?person?Speakerperson?=?wlperson.Say("Hello?World!")person?=?jlperson.Say("Good?Luck!") }


    Speaker接口有兩個實現WangLan類型和JiangLou類型。但是具體到實例來說,變量wl和變量jl只有是對應實例的指針類型才真正能被Speaker接口變量所持有。這是因為WangLan類型和JiangLou類型所有對Speaker接口的實現都是在*T上。這就是上例中person能夠持有wl和jl的原因。

    想象一下java的泛型(很可惜golang不支持泛型),java在支持泛型之前需要手動裝箱和拆箱。由于golang能將不同的類型存入到接口類型的變量中,使得問題變得更加復雜。所以有時候我們不得不面臨這樣一個問題:我們究竟往接口存入的是什么樣的類型?有沒有辦法反向查詢?答案是肯定的。

    Comma-ok斷言的語法是:value, ok := element.(T)。element必須是接口類型的變量,T是普通類型。如果斷言失敗,ok為false,否則ok為true并且value為變量的值。來看個例子:

    package?mainimport?("fmt")type?Html?[]interface{}func?main()?{html?:=?make(Html,?5)html[0]?=?"div"html[1]?=?"span"html[2]?=?[]byte("script")html[3]?=?"style"html[4]?=?"head"for?index,?element?:=?range?html?{ if?value,?ok?:=?element.(string);?ok?{fmt.Printf("html[%d]?is?a?string?and?its?value?is?%s\n",?index,?value)}?else?if?value,?ok?:=?element.([]byte);?ok?{fmt.Printf("html[%d]?is?a?[]byte?and?its?value?is?%s\n",?index,?string(value))}} }


    其實Comma-ok斷言還支持另一種簡化使用的方式:value := element.(T)。但這種方式不建議使用,因為一旦element.(T)斷言失敗,則會產生運行時錯誤。如:

    package?mainimport?("fmt")func?main()?{ var?val?interface{}?=?"good"fmt.Println(val.(string)) //?fmt.Println(val.(int))}


    以上的代碼中被注釋的那一行會運行時錯誤。這是因為val實際存儲的是string類型,因此斷言失敗。

    還有一種轉換方式是switch測試。既然稱之為switch測試,也就是說這種轉換方式只能出現在switch語句中。可以很輕松的將剛才用Comma-ok斷言的例子換成由switch測試來實現:

    package?mainimport?("fmt" )type?Html?[]interface{}func?main()?{ html?:=?make(Html,?5)html[0]?=?"div"html[1]?=?"span"html[2]?=?[]byte("script")html[3]?=?"style"html[4]?=?"head"for?index,?element?:=?range?html?{switch?value?:=?element.(type)?{case?string:fmt.Printf("html[%d]?is?a?string?and?its?value?is?%s\n",?index,?value)case?[]byte:fmt.Printf("html[%d]?is?a?[]byte?and?its?value?is?%s\n",?index,?string(value))case?int:fmt.Printf("invalid?type\n")default:fmt.Printf("unknown?type\n")}} }


    $?cd?$GOPATH/src/typeassert_test$?go?build$?./typeassert_test



    轉載于:https://blog.51cto.com/13914991/2294030

    總結

    以上是生活随笔為你收集整理的golang: 类型转换和类型断言的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。