x.(T)
檢查x的動態(tài)類型是否是T呐能,其中x必須是接口值往堡。
- 如果T是具體類型
類型斷言檢查x的動態(tài)類型是否等于具體類型T坝疼。如果檢查成功乙埃,類型斷言返回的結(jié)果是x的動態(tài)值,其類型是T锯岖。換句話說介袜,對接口值x斷言其動態(tài)類型是具體類型T,若成功則提取出x的具體值出吹。如果檢查失敗則panic遇伞。
例如:
var w io.Writer
w = os.Stdout //os.Stdout是一個類型為*os.File的包級別變量
f := w.(*os.File) //斷言接口w的動態(tài)類型是具體類型*os.File,斷言成功洞拨,返回os.Stdout給f
c := w.(*bytes.Buffer) //斷言接口w的動態(tài)類型是具體類型*bytes.Buffer询吴,斷言失敗,panic
- 如果T是接口類型
類型斷言檢查x的動態(tài)類型是否滿足T嗜诀。如果檢查成功渐排,x的動態(tài)值不會被提取炬太,返回值是一個類型為T的接口值。換句話說驯耻,到接口類型的類型斷言亲族,改變了表達式的類型,改變了(通常是擴大了)可以訪問的方法可缚,且保護了接口值內(nèi)部的動態(tài)類型和值霎迫。
例如:
var w io.Writer
w = os.Stdout
rw := w.(io.ReadWriter) //成功:*os.File同時有Read和Write方法
w = new(ByteCounter) //有Write方法
rw = w.(io.ReadWriter) // panic: *ByteCounter沒有Read方法
- 無論T是什么類型,如果x是nil接口值帘靡,則類型斷言失敗知给。
- 類型斷言到一個較少限制(較少方法)的接口類型基本是不需要的,因為這個行為和賦值一樣(除了nil的情況):
w = rw // io.ReadWriter賦值給io.Writer
w = rw.(io.Writer) //和上面一樣描姚,僅當rw為nil時失敗涩赢,而上面如果rw為nil則w被賦值為nil
如果我們想知道類型斷言是否失敗,而不是失敗時觸發(fā)panic轰胁,可以使用返回兩個值的版本:
y, ok := x.(T)
當檢查成功時ok為true谒主。例如:
var w io.Writer = os.Stdout
f, ok := w.(*os.File) //成功:f為os.Stdout,ok為true
b, ok := w.(*bytes.Buffer) //失斣叻А:b為零值霎肯,這里是nil, ok為false榛斯,no panic
ok值通常立刻用于決定是否執(zhí)行下一步观游,慣用法:
if f, ok := w.(*os.File); ok {
// ... use f ...
}
類型斷言用于查詢可能的行為,例子:
package main
import (
"io"
"os"
)
//這個例子展示了類型斷言用于選擇可能的行為驮俗,這兒如果一個io.Writer支持WriteString則可以直接使用懂缕,從而避免分配臨時內(nèi)存
func writeString(w io.Writer, s string) (n int, err error) {
type stringWriter interface {
WriteString(string) (n int, err error)
}
if sw, ok := w.(stringWriter); ok {
//fmt.Print("<use WriteString>")
return sw.WriteString(s) // avoid temporary copy
}
return w.Write([]byte(s)) // allocate temporary copy
}
func writeHeader(w io.Writer, contentType string) error {
if _, err := writeString(w, "Content-Type: "); err != nil {
return err
}
if _, err := io.WriteString(w, contentType); err != nil { //系統(tǒng)自帶的io.WriteString實現(xiàn)和上面一樣
return err
}
return nil
}
func main() {
writeHeader(os.Stdout, "test")
}
- 接口值可有包含各種不同的具體類型值,而類型斷言就是用于從接口中動態(tài)的區(qū)分出各種具體的類型王凑,從而可以使用具體的類型搪柑。
- Type Switches
switch x.(type){
case nil: // 如果x是nil
case int, uint:
case bool:
case string;
default: //沒有匹配上
}
//case的順序是有意義的,因為可能同時滿足多個接口索烹,不可以用fallthrough, default的位置無所謂工碾。
如果需要提取具體的值:
switch x := x.(type) { /* ... */ } //前面的x是一個局部變量,因為switch創(chuàng)建了一個詞法域
//x的類型就是每個case的類型百姓,如果case有多個類型渊额,則類型為interface{}