這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。![](https://raw.githubusercontent.com/studygolang/gctt-images/master/type-assertion/1_p6c6i0niHNOIlRbsAhD3lA.jpeg)<center>[https://en.wikipedia.org/wikiPsycho_(1960_film)](https://en.wikipedia.org/wiki/Psycho_%281960_film%29)</center>類型斷言被用於檢查介面類型變數所持有的值是否實現了期望的介面或者具體的類型。類型斷言的文法定義如下:```goPrimaryExpression.(Type)```PrimaryExpression 可以在[ Go 語言規範](https://golang.org/ref/spec#PrimaryExpr)中找到,並且它可以是標識符,特定索引的數組元素,切片等等。Type 既可以是類型標識符,也可以是類型字面量,比如:```gotype I interface {walk()quack()}type S struct{}func (s S) walk() {}func (s S) quack() {}var i Ii = S{}fmt.Println(i.(interface {walk()}))```PrimaryExpression 必須是介面類型,否則就會產生一個編譯時間錯誤:```gotype I interface{walk()quack()}type S struct{}S{}.(I) // 無效類型斷言:S{}.(I)(操作符左邊的 S 並不是個介面類型)```> 如果運算式為 nil,類型斷言就不會成立。## 動態類型變數除了有靜態類型外(變數聲明中的類型),介面變數還有動態類型。就是在當前介面類型變數中設定的一種類型的值。在程式執行的過程當中,介面類型的變數具有相同的靜態類型,但是其動態類型會隨著其實現的介面不同,而其值也會隨之改變。```gotype I interface {walk()}type A struct{}func (a A) walk() {}type B struct{}func (b B) walk() {}func main() {var i Ii = A{} // i 的動態類型是 Afmt.Printf("%T\n", i.(A))i = B{} // i 的動態類型是 Bfmt.Printf("%T\n", i.(B))}```## 介面類型如果 T 來自 v.(T) 是一個介面類型,這樣的斷言檢查,可以用來檢測 v 的動態類型是否實現了介面 T:```gotype I interfacce {walk()}type J interface {quack()}type K interface {bark()}type S struc{}func (s S) walk() {}func (s S) quack() {}func main() {var i Ii = S{}fmt.Printf("%T\n", i.(J))fmt.Printf("%T\n", i.(K)) // panic: 介面轉換: main.S 不是 main.K: 缺少方法 bark}```## 非介面類型如果 T 來自 v.(T) 不是介面類型,這樣斷言檢查動態類型 v 是否與 T 類型相同:```gotype I interface {walk()}type A struct{}func (a A) walk() {}type B struct{}func (b B) walk() {}func main() {var i Ii = A{}fmt.Printf("%T\n", i.(A))fmt.Printf("%T\n", i.(B)) // panic: 介面轉換: main.I 是 main.A, 不是 main.B}```在非介面類型情況下進行類型傳遞就必須實現介面 I,如果不滿足這個要求的話就會在編譯時間被捕獲:```gotype C struct{}fmt.Prinf("%T\n", i.(C))```輸出:> impossible type assertion: > C does not implement I (missing walk method)## 不要 panic在上述情況下,當斷言不能成立時,運行時 panic 將會被觸發。為了優雅的處理錯誤,這裡有特殊的形式來賦值或者初始化:```gotype I interface {walk()}type A struct {name string}func (a A) walk() {}type B struct {name string}func (b B) walk() {}func main() {var i Ii = A{name: "foo"}valA, okA := i.(A)fmt.Printf("%#v %#v\n", valA, okA)valB, okB := i.(B)fmt.Printf("%#v %#v\n", valB, okB)}```輸出:```bashmain.A{name:"foo"} truemain.B{name:""} false```> 當斷言不成立時,第一個值將會作為測試類型的[零值](https://golang.org/ref/spec#The_zero_value)## 資源:* [go 程式設計語言規範- go 程式設計語言](https://golang.org/ref/spec#TypeAssertion)* [Go 是一個通用語言,設計時考慮了系統編程。它是強型別的並且具有記憶體回收機制...](https://golang.org/ref/spec#TypeAssertion)* [golang.org](https://golang.org/ref/spec#TypeAssertion)
via: https://medium.com/golangspec/type-assertions-in-go-e609759c42e1
作者:Michał Łowicki 譯者:fredvence 校對:rxcai
本文由 GCTT 原創編譯,Go語言中文網 榮譽推出
本文由 GCTT 原創翻譯,Go語言中文網 首發。也想加入譯者行列,為開源做一些自己的貢獻嗎?歡迎加入 GCTT!
翻譯工作和譯文發表僅用於學習和交流目的,翻譯工作遵照 CC-BY-NC-SA 協議規定,如果我們的工作有侵犯到您的權益,請及時聯絡我們。
歡迎遵照 CC-BY-NC-SA 協議規定 轉載,敬請在本文中標註並保留原文/譯文連結和作者/譯者等資訊。
文章僅代表作者的知識和看法,如有不同觀點,請樓下排隊吐槽
342 次點擊