Go語言斷言和類型查詢的實現(xiàn)
1、類型斷言
類型斷言(Type Assertion)是一個使用在接口值上的操作,用于檢查接口類型變量所持有的值是否實現(xiàn)了期望的接口或者具體的類型。
在Go語言中類型斷言的語法格式如下:
// i.(TypeNname) value, ok := x.(T)
其中,x 表示一個接口的類型,如果是具體類型變量,則編譯器會報non-interface type xxx on left
,T 表示一個具體的類型(也可為接口類型)。
該斷言表達式會返回 x 的值(也就是 value)和一個布爾值(也就是 ok),可根據該布爾值判斷 x 是否為 T 類型:
如果 T 是具體某個類型,類型斷言會檢查 x 的動態(tài)類型是否等于具體類型 T。如果檢查成功,類型斷言返回的結果是 x 的動態(tài)值,其類型是 T。
如果 T 是接口類型,類型斷言會檢查 x 的動態(tài)類型是否滿足 T。如果檢查成功,x 的動態(tài)值不會被提取,返回值是一個類型為 T 的接口值。
無論 T 是什么類型,如果 x 是 nil 接口值,類型斷言都會失敗。
示例代碼如下:
package main import ( "fmt" ) func main() { var x interface{} x = 10 value, ok := x.(int) // 10,true fmt.Print(value, ",", ok) }
運行結果如下:
# 程序結果
10,true
需要注意如果不接收第二個參數也就是上面代碼中的 ok,斷言失敗時會直接造成一個 panic,如果 x 為 nil 同樣也會 panic。
示例代碼如下:
package main import ( "fmt" ) func main() { var x interface{} x = "Hello" value := x.(int) fmt.Println(value) }
運行結果如下:
# 輸出結果
panic: interface conversion: interface {} is string, not int
接口斷言通??梢允褂?comma,ok 語句來確定接口是否綁定某個實例類型,或者判斷接口綁定的實例類型是否實現(xiàn)另一個接口。
re,ok := body.(io.ReadCloser) if,ok := r.Body.(*maxBytesReader);
2、類型查詢
接口類型查詢的語法格式如下:
switch v := i.(type){ case typel: XXXX case type2: XXXX default: XXXX }
接口查詢有兩層語義,一是查詢一個接口變量底層綁定的底層變量的具體類型是什么,二是查詢接口變量綁定的底
層變量是否還實現(xiàn)了其他接口。
(1)、i 必須是接口類型
具體類型實例的類型是靜態(tài)的,在類型聲明后就不再變化,所以具體類型的變量不存在類型查詢,類型查詢一定是
對一個接口變量進行操作。也就是說,上文中的i必須是接口變量,如果i是未初始化接口變量,則v的值是 nil。
package main import ( "fmt" "io" ) func main() { var i io.Reader //此處i是為未初始化的接口變量,所以v為nil switch v := i.(type) { case nil: //<nil> fmt.Printf("%T\n", v) default: fmt.Printf("default") } }
(2)、case 字句后面可以跟非接口類型名,也可以跟接口類型名,匹配是按照 case 子句的順序進行的。
如果 case 后面是一個接口類型名,且接口變量i綁定的實例類型實現(xiàn)了該接口類型的方法,則匹配成,v的類型是接口類型,v底層綁定的實例是i綁定具體類型實例的副本。
如果 case 后面是一個具體類型名,且接口變量i綁定的實例類型和該具體類型相同,則匹配成功,此時v 就是該具體類型變量,v的值是i綁定的實例值的副本。
如果 case 后面跟著多個類型,使用逗號分隔,接口變量i綁定的實例類型只要和其中一個類型匹配,則直接使用o賦值給v,相當于 v:=o。這個語法有點奇怪,按理說編譯器不應該允許這種操作,語言實現(xiàn)者可能想讓type switch 語句和普通的 switch 語句保持一樣的語法規(guī)則,允許發(fā)生這種情況。
如果所有的case字句都不滿足,則執(zhí)行 default 語句,此時執(zhí)行的仍然是 v:=o,最終v的值是o。此時使用v沒有任何意義。
fallthrough 語句不能在 Type Switch 語句中使用。
package main import ( "fmt" "io" "log" "os" ) func main() { var i io.Reader // 此處i是為未初始化的接口變量,所以v為nil switch v := i.(type) { case nil: // <nil> fmt.Printf("%T\n", v) default: fmt.Printf("default") } f, err := os.OpenFile("notes.txt", os.O_RDWR|os.O_CREATE, 0755) if err != nil { log.Fatal(err) } defer f.Close() i = f switch v := i.(type) { // i的綁定的實例是*osFile類型,實現(xiàn)了io.ReadWriter接口,所以下面case匹配成功 case io.ReadWriter: // v是io.ReadWriter接口類型,所以可以調用Write方法 v.Write([]byte("io.ReadWriter\n")) // Type Switch Result: io.ReadWriter fmt.Println("Type Switch Result: io.ReadWriter") // 由于上一個case已經匹配,就算這個case也匹配,也不會走到這里 case *os.File: v.Write([]byte("*os.File\n")) fmt.Println("Type Switch Result: *os.File") //這里可以調用具體類型方法 v.Sync() default: fmt.Println("Type Switch Result: unknown") return } switch v := i.(type) { // 匹配成功,v的類型就是具體類型*os.File case *os.File: v.Write([]byte("*os.File\n")) // Type Switch Result: *os.File fmt.Println("Type Switch Result: *os.File") v.Sync() //由于上一個case已經匹配,就算這個case也匹配,也不會走到這里 case io.ReadWriter: //v是io.ReadWriter接口類型,所以可以調用Write方法 v.Write([]byte("io.ReadWriter\n")) fmt.Println("Type Switch Result: io.ReadWriter") default: fmt.Println("Type Switch Result: unknown") return } switch v := i.(type) { //多個類型,f滿足其中任何一個就算匹配 case *os.File, io.ReadWriter: // 此時相當于執(zhí)行力v := i ,v和i是等價的,使用v沒有意義。 if v == i { // true fmt.Println(true) } default: return } }
# 程序輸出
<nil>
Type Switch Result: io.ReadWriter
Type Switch Result: *os.File
true
package main import ( "fmt" ) func main() { var a int a = 10 // the type of a is int getType(a) } func getType(a interface{}) { switch a.(type) { case int: fmt.Println("the type of a is int") case string: fmt.Println("the type of a is string") case float64: fmt.Println("the type of a is float") default: fmt.Println("unknown type") } }
# 程序輸出
the type of a is int
到此這篇關于Go語言斷言和類型查詢的實現(xiàn)的文章就介紹到這了,更多相關Go語言斷言和類型查詢內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!