go語言的類型斷言(Type Assertion)

x.(T) 檢查x的動(dòng)態(tài)類型是否是T,其中x必須是接口值。

  • 如果T是具體類型
    類型斷言檢查x的動(dòng)態(tài)類型是否等于具體類型T。如果檢查成功,類型斷言返回的結(jié)果是x的動(dòng)態(tài)值,其類型是T。換句話說,對接口值x斷言其動(dòng)態(tài)類型是具體類型T,若成功則提取出x的具體值。如果檢查失敗則panic。
    例如:
var w io.Writer
w = os.Stdout  //os.Stdout是一個(gè)類型為*os.File的包級別變量
f := w.(*os.File) //斷言接口w的動(dòng)態(tài)類型是具體類型*os.File,斷言成功,返回os.Stdout給f
c := w.(*bytes.Buffer) //斷言接口w的動(dòng)態(tài)類型是具體類型*bytes.Buffer,斷言失敗,panic
  • 如果T是接口類型
    類型斷言檢查x的動(dòng)態(tài)類型是否滿足T。如果檢查成功,x的動(dòng)態(tài)值不會(huì)被提取,返回值是一個(gè)類型為T的接口值。換句話說,到接口類型的類型斷言,改變了表達(dá)式的類型,改變了(通常是擴(kuò)大了)可以訪問的方法,且保護(hù)了接口值內(nèi)部的動(dòng)態(tài)類型和值。
    例如:
var w io.Writer
w = os.Stdout
rw := w.(io.ReadWriter) //成功:*os.File同時(shí)有Read和Write方法
w = new(ByteCounter)  //有Write方法
rw = w.(io.ReadWriter) // panic: *ByteCounter沒有Read方法
  • 無論T是什么類型,如果x是nil接口值,則類型斷言失敗。
  • 類型斷言到一個(gè)較少限制(較少方法)的接口類型基本是不需要的,因?yàn)檫@個(gè)行為和賦值一樣(除了nil的情況):
w = rw // io.ReadWriter賦值給io.Writer
w = rw.(io.Writer) //和上面一樣,僅當(dāng)rw為nil時(shí)失敗,而上面如果rw為nil則w被賦值為nil

如果我們想知道類型斷言是否失敗,而不是失敗時(shí)觸發(fā)panic,可以使用返回兩個(gè)值的版本:

y, ok := x.(T)

當(dāng)檢查成功時(shí)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"
)

//這個(gè)例子展示了類型斷言用于選擇可能的行為,這兒如果一個(gè)io.Writer支持WriteString則可以直接使用,從而避免分配臨時(shí)內(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實(shí)現(xiàn)和上面一樣
        return err
    }
    return nil
}

func main() {
    writeHeader(os.Stdout, "test")
}

  • 接口值可有包含各種不同的具體類型值,而類型斷言就是用于從接口中動(dòng)態(tài)的區(qū)分出各種具體的類型,從而可以使用具體的類型。

  • Type Switches
switch x.(type){
case nil: // 如果x是nil
case int, uint: 
case bool:
case string;
default: //沒有匹配上
}
//case的順序是有意義的,因?yàn)榭赡芡瑫r(shí)滿足多個(gè)接口,不可以用fallthrough, default的位置無所謂。

如果需要提取具體的值:

switch x := x.(type) { /* ... */  } //前面的x是一個(gè)局部變量,因?yàn)閟witch創(chuàng)建了一個(gè)詞法域
//x的類型就是每個(gè)case的類型,如果case有多個(gè)類型,則類型為interface{}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,969評論 19 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,767評論 18 399
  • 第一次知道反射的時(shí)候還是許多年前在學(xué)校里玩 C# 的時(shí)候。那時(shí)總是弄不清楚這個(gè)復(fù)雜的玩意能有什么實(shí)際用途……然后發(fā)...
    勿以浮沙筑高臺(tái)閱讀 1,143評論 0 9
  • 方法和接口 第四篇包含了方法和接口,可以用它們來定義對象和其行為;以及如何將所有內(nèi)容貫通起來。 方法 Go 沒有類...
    張洋銘Ocean閱讀 1,494評論 2 0
  • 那時(shí),獨(dú)居鄉(xiāng)間一個(gè)斗室 每個(gè)秋天的夜晚,總有 一樹桂影婆娑窗前 一叢桐葉,搖曳著一地月光 斗室里,沒有家具 沒有音...
    甘樹林閱讀 973評論 8 4