• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            Fork me on GitHub
            隨筆 - 215  文章 - 13  trackbacks - 0
            <2025年7月>
            293012345
            6789101112
            13141516171819
            20212223242526
            272829303112
            3456789


            專注即時(shí)通訊及網(wǎng)游服務(wù)端編程
            ------------------------------------
            Openresty 官方模塊
            Openresty 標(biāo)準(zhǔn)模塊(Opm)
            Openresty 三方模塊
            ------------------------------------
            本博收藏大部分文章為轉(zhuǎn)載,并在文章開頭給出了原文出處,如有再轉(zhuǎn),敬請保留相關(guān)信息,這是大家對原創(chuàng)作者勞動(dòng)成果的自覺尊重??!如為您帶來不便,請于本博下留言,謝謝配合。

            常用鏈接

            留言簿(1)

            隨筆分類

            隨筆檔案

            相冊

            Awesome

            Blog

            Book

            GitHub

            Link

            搜索

            •  

            積分與排名

            • 積分 - 216887
            • 排名 - 118

            最新評(píng)論

            閱讀排行榜

            http://blog.csdn.net/kai_ding/article/details/25948461


            本文為轉(zhuǎn)載,原文地址:http://blog.go-china.org/22-type-assert

            類型轉(zhuǎn)換在程序設(shè)計(jì)中都是不可避免的問題。當(dāng)然有一些語言將這個(gè)過程給模糊了,大多數(shù)時(shí)候開發(fā)者并不需要去關(guān)注這方面的問題。但是golang中的類型匹配是很嚴(yán)格的,不同的類型之間通常需要手動(dòng)轉(zhuǎn)換,編譯器不會(huì)代你去做這個(gè)事。我之所以說通常需要手動(dòng)轉(zhuǎn)換,是因?yàn)閕nterface類型作為一個(gè)特例,會(huì)有不同的處理方式。

            golang中的所有類型都有自己的默認(rèn)值,對此我做了個(gè)測試

            $GOPATH/src

            —-typeassert_test

            ——–main.Go

            main.go的代碼如下:

            package main
            
            import (
                "fmt"
            )
            
            type myStruct struct {
                name   bool
                userid int64
            }
            
            var structZero myStruct
            var intZero int
            var int32Zero int32
            var int64Zero int64
            var uintZero uint
            var uint8Zero uint8
            var uint32Zero uint32
            var uint64Zero uint64
            var byteZero byte
            var boolZero bool
            var float32Zero float32
            var float64Zero float64
            var stringZero string
            var funcZero func(int) int
            var byteArrayZero [5]byte
            var boolArrayZero [5]bool
            var byteSliceZero []byte
            var boolSliceZero []bool
            var mapZero map[string]bool
            var interfaceZero interface{}
            var chanZero chan int
            var pointerZero *int
            
            func 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
            

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

            對于字面量的值,編譯器會(huì)有一個(gè)隱式轉(zhuǎn)換??聪旅娴睦樱?/p>

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

            對于myInt變量,它存儲(chǔ)的就是int32類型的5;對于myFloat變量,它存儲(chǔ)的是int64類型的0?;蛟S您可能會(huì)寫出這樣的代碼,但確實(shí)不是必須這么做的:

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

            在C中,大多數(shù)類型轉(zhuǎn)換都是可以隱式進(jìn)行的,比如:

            #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中,您不能這么做。有個(gè)類似的例子:

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

            很顯然,將uid賦值給gid之前,需要將uid強(qiáng)制轉(zhuǎn)換成int64類型,否則會(huì)panic。golang中的類型區(qū)分靜態(tài)類型和底層類型。您可以用type關(guān)鍵字定義自己的類型,這樣做的好處是可以語義化自己的代碼,方便理解和閱讀。

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

            在上面的代碼中,定義了一個(gè)新的類型MyInt32。對于類型MyInt32來說,MyInt32是它的靜態(tài)類型,int32是它的底層類型。即使兩個(gè)類型的底層類型相同,在相互賦值時(shí)還是需要強(qiáng)制類型轉(zhuǎn)換的??梢杂胷eflect包中的Kind方法來獲取相應(yīng)類型的底層類型。

            對于類型轉(zhuǎn)換的截?cái)鄦栴},為了問題的簡單化,這里只考慮具有相同底層類型之間的類型轉(zhuǎn)換。小類型(這里指存儲(chǔ)空間)向大類型轉(zhuǎn)換時(shí),通常都是安全的。下面是一個(gè)大類型向小類型轉(zhuǎn)換的示例:

            package main
            
            import (
                "fmt"
            )
            
            func main() {
                var gid int32 = 0x12345678
                var uid int8  = int8(gid)
                fmt.Printf("uid=0x%02x, gid=0x%02x\n", uid, gid)
            }
            

            在上面的代碼中,gid為int32類型,也即占4個(gè)字節(jié)空間(在內(nèi)存中占有4個(gè)存儲(chǔ)單元),因此這4個(gè)存儲(chǔ)單元的值分別是:0x12, 0x34, 0x56, 0x78。但事實(shí)不總是如此,這跟cpu架構(gòu)有關(guān)。在內(nèi)存中的存儲(chǔ)方式分為兩種:大端序和小端序。大端序的存儲(chǔ)方式是高位字節(jié)存儲(chǔ)在低地址上;小端序的存儲(chǔ)方式是高位字節(jié)存儲(chǔ)在高地址上。本人的機(jī)器是按小端序來存儲(chǔ)的,所以gid在我的內(nèi)存上的存儲(chǔ)序列是這樣的:0x78, 0x56, 0x34, 0x12。如果您的機(jī)器是按大端序來存儲(chǔ),則gid的存儲(chǔ)序列剛好反過來:0x12, 0x34, 0x56, 0x78。對于強(qiáng)制轉(zhuǎn)換后的uid,肯定是產(chǎn)生了截?cái)嘈袨?。因?yàn)閡id只占1個(gè)字節(jié),轉(zhuǎn)換后的結(jié)果必然會(huì)丟棄掉多余的3個(gè)字節(jié)。截?cái)嗟囊?guī)則是:保留低地址上的數(shù)據(jù),丟棄多余的高地址上的數(shù)據(jù)。來看下測試結(jié)果:

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

            如果您的輸出結(jié)果是:

            uid=0x12, gid=0x12345678
            

            那么請不要驚訝,因?yàn)槟臋C(jī)器是屬于大端序存儲(chǔ)。

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

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

            接口的轉(zhuǎn)換遵循以下規(guī)則:

            普通類型向接口類型的轉(zhuǎn)換是隱式的。

            接口類型向普通類型轉(zhuǎn)換需要類型斷言。

            普通類型向接口類型轉(zhuǎn)換的例子隨處可見,例如:

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

            正如您所預(yù)料的,”hello”作為string類型存儲(chǔ)在interface{}類型的變量val中,[]byte{‘a’, ‘b’, ‘c’}作為slice存儲(chǔ)在interface{}類型的變量val中。這個(gè)過程是隱式的,是編譯期確定的。

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

            package main
            
            import (
                "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 Speaker
                person = wl
                person.Say("Hello World!")
                person = jl
                person.Say("Good Luck!")
            }
            

            Speaker接口有兩個(gè)實(shí)現(xiàn)WangLan類型和JiangLou類型。但是具體到實(shí)例來說,變量wl和變量jl只有是對應(yīng)實(shí)例的指針類型才真正能被Speaker接口變量所持有。這是因?yàn)閃angLan類型和JiangLou類型所有對Speaker接口的實(shí)現(xiàn)都是在*T上。這就是上例中person能夠持有wl和jl的原因(不明白的可以看看我的其他相關(guān)博客)。

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

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

            package main
            
            import (
                "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))
                    }
                }
            }
            

            其實(shí)Comma-ok斷言還支持另一種簡化使用的方式:value := element.(T)。但這種方式不建議使用,因?yàn)橐坏〆lement.(T)斷言失敗,則會(huì)產(chǎn)生運(yùn)行時(shí)錯(cuò)誤。如:

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

            以上的代碼中被注釋的那一行會(huì)運(yùn)行時(shí)錯(cuò)誤。這是因?yàn)関al實(shí)際存儲(chǔ)的是string類型,因此斷言失敗。

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

            package main
            
            import (
                "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("error type\n")
                    default:
                        fmt.Printf("unknown type\n")
                    }
                }
            }
            
            $ cd $GOPATH/src/typeassert_test
            $ go build
            $ ./typeassert_test
            

            posted on 2017-05-04 10:30 思月行云 閱讀(247) 評(píng)論(0)  編輯 收藏 引用 所屬分類: Golang
            久久久久久一区国产精品| 国产精品热久久毛片| 久久男人中文字幕资源站| 国内精品久久久久久99| 亚洲中文久久精品无码| 欧美色综合久久久久久| 久久精品国产99久久香蕉| 一本久久久久久久| 91精品国产高清91久久久久久| 久久丝袜精品中文字幕| 久久九九免费高清视频| 久久久久国产精品三级网| 久久免费视频6| 97精品依人久久久大香线蕉97| 久久中文字幕人妻丝袜| 超级97碰碰碰碰久久久久最新| 人人狠狠综合久久亚洲高清| 伊人久久大香线蕉AV一区二区| 亚洲欧美另类日本久久国产真实乱对白 | 精品一区二区久久| 99久久婷婷国产综合精品草原| 亚洲国产精久久久久久久| 国产L精品国产亚洲区久久 | 久久露脸国产精品| 人人妻久久人人澡人人爽人人精品| 久久精品青青草原伊人| 人妻无码久久一区二区三区免费 | 久久久久亚洲精品无码蜜桃| 国内精品久久久久影院优| 国产99久久久国产精品~~牛| 97精品伊人久久大香线蕉| 97精品国产91久久久久久| 精品国产综合区久久久久久| 亚洲人成电影网站久久| 91精品国产综合久久久久久| 久久精品亚洲欧美日韩久久| 午夜久久久久久禁播电影| 精品人妻伦九区久久AAA片69| 伊人久久一区二区三区无码| 国产精品久久久久9999| 伊人久久大香线蕉无码麻豆|