Go 中的介面

2021-09-25 03:30:24 字數 2759 閱讀 9293

很多物件導向的語言都有介面這個概念。go 語言的介面的獨特之處在於它是隱式實現。換句話說,對於乙個具體的型別,無序宣告它實現了哪些介面,只要提供介面所必需的方法即可。

乙個介面型別定義了一套方法,如果乙個具體型別要實現該介面,那麼必須實現介面型別定義的所有方法。

如果乙個型別實現了乙個介面所要求的所有方法,那麼這個型別就實現了這個介面。

var w io.writer

w = os.stdout // ok: *os.file 有 write 方法

w =new(bytes.buffer)

// ok: *bytes.buffer 有 write 方法

w = time.second // 編譯錯誤: time.duration 缺少 write 方法

var rwc io.readwritecloser

rwc = os.stdout // ok: *os.file 有 read、write、close 方法

rwc =

new(bytes.buffer)

// 編譯錯誤: *bytes.buffer 缺少 close 方法

// 當右側表示式也是乙個介面時,該規則也有效:

w = rwc

rwc = w // 編譯錯誤:io.writer 缺少 close 方法

從概念上來講,乙個介面型別的值(簡稱介面值)其實有兩個部分:乙個具體型別和該型別的乙個值。二者稱為介面的動態型別和動態值。

介面值可以用 == 和 != 操作符來做比較。如果兩個介面值都是 nil 或者二者的動態型別完全一致且二者動態值相等(使用動態型別的 == 操作符來做比較),那麼兩個介面值相等。因為介面值是可以比較的,所以它們可以作為 map 的鍵,也可以作為 switch 語句的運算元。

需要注意的是,在比較兩個介面值時,如果兩個介面值的動態型別一致,但對應的動態值是不可比較的(比如 slice),那麼這個比較會以崩潰的方式失敗:

var x inte***ce=[

]int

fmt.

println

(x == x)

// 宕機:試圖比較不可比較的型別 int

型別斷言是乙個作用在介面值上的操作,寫出來類似於 x.(t),其中 x 是乙個介面型別的表示式,而 t 是乙個型別(稱為斷言型別)。型別斷言會檢查作為運算元的動態型別是否滿足指定的斷言型別。

這兒有兩個可能。首先,如果斷言型別t是乙個具體型別,那麼型別斷言會檢查 x 的動態型別是否就是 t。如果檢查成功,型別斷言的結果就是 x 的動態值,型別當然就是t。換句話說,型別斷言就是用來從它的運算元中把具體的型別提取出來的操作。

var w io.writer

w = os.stdout

f := w.

(*os.file)

// 成功: f == os.stdout

c := w.

(*bytes.buffer)

// 崩潰:介面持有的是 *os.file, 不是 *bytes.buffer

其次,如果斷言型別t 是乙個介面型別,那麼型別斷言檢查 x 的動態型別是否滿足 t。如果檢查成功,動態值並沒有提取出來,結果仍然是乙個介面值。

考慮一下os 包中的檔案操作返回的錯誤集合, i/o 會因為很多原因失敗,但有三類原因通常必須單獨處理:檔案已儲存(建立操作),檔案沒找到(讀取操作)以及許可權不足。os 包提供了三個幫助函式用來對錯誤進行分類:

package os

func

i***ist

(err error

)bool

func

isnotexist

(err error

)bool

func

ispermission

(err error

)bool

func

isnotexist

(err error

)bool

但由於處理 i/o 錯誤的邏輯會隨著平台的變化而變化,因此這種方式很不健壯,同樣的錯誤可能會用完全不同的錯誤訊息來報告。

乙個更可靠的方法是用專門的型別來表示結構化的錯誤值。os 包定義了乙個 patherror 型別來表示在與乙個檔案路徑相關的操作上發生錯誤(比如 open 或者 delete),乙個類似的 linkerror 用來表述在於兩個檔案路徑相關的操作上發生的錯誤(比如 symlink 和 rename).

var errnotexist = errors.

new(

"file dose not exist"

)// isnotexist 返回乙個布林值,該值表明錯誤是否代表檔案或目錄不存在

func

isnotexist

(err error

)bool

return err == syscall.enoent || err == errnotexist

}

型別分支的最簡單形式與普通分支語句類似,兩個的差別是運算元改為 x.(type)

var x inte***ce

switch x.

(type

)

與普通的 switch 語句類似,分支是按順序來判定的,當乙個分支符合時,對應的**會執行。分支的順序在乙個或多個介面型別時變得很重要,因為有可能兩個分支都能滿足。default 分支的位置無關緊要。另外,型別分支不允許使用 fallthrough。

Go 介面,介面繼承

demo.go 介面繼承 package main import fmt 父介面 type humen inte ce 子介面 type person inte ce 學生類 type student struct 學生類的方法 讓學生類符合父介面的規則 func stu student sayhe...

Go語言的介面

介面是一種抽象型別,是對其他型別行為的概括與抽象,從語法角度來看,介面是一組方法定義的集合。很多物件導向的語言都有介面這個概念,但go語言介面的獨特之處在於它是隱式實現。換句話說,對於乙個具體的型別,無須宣告它實現了哪些介面,只要提供介面所必需的方法即可。這種設計讓程式設計人員無須改變已有型別的實現...

Go的介面總結

一 什麼是介面 二 什麼是介面值 乙個介面值可以持有任意大的動態值,不論動態值多大,介面值總是可以容下它 介面值的可比較性 注意 乙個包含nil指標的介面不是nil介面 空介面 此時呼叫介面方法會發生panic錯誤。即乙個介面值的動態型別type nil,但動態值value nil,此時的介面值 w...