Go語言的介面

2021-09-10 16:48:01 字數 2957 閱讀 7449

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

以go標準庫中的fmt.printf和fmt.sprintf為例。前者把結果傳送到標準輸出,後者把結果以string型別返回。輸出的實現中格式化是最複雜的部分,通過介面機制可以復用這部分實現。其實兩個函式都封裝了第三個函式fmt.fprintf,這個函式對結果輸出到**並不關心:

package fmt

func fprintf(w io.writer, format string, args ...inte***ce{}) (int, error)

func printf(format string, args ...inte***ce{}) (int, error)

func sprintf(format string, args ...inte***ce{}) string

fprintf的第乙個引數w是介面型別,其宣告如下:

package io

type writer inte***ce

io.writer介面定義了fprintf和呼叫者之間的約定。一方面,這個約定要求呼叫者提供的具體型別(如printf中的*os.file型別的引數os.stdout或sprintf中的*bytes.buffer型別的引數&buf)包含乙個與其簽名和行為一致的write方法。另一方面,這個約定保證了fprintf能夠使用任何滿足io.writer介面的引數。fprintf只需要能呼叫引數的write函式,無須假設它寫入的是乙個檔案還是一段記憶體。

我們也可以建立乙個新型別來實現io.writer介面:

type bytecounter int

func (c *bytecounter) write(p byte) (int, error)

因為*bytecounter滿足io.writer介面的約定,所以可以在fpirntf中使用它:

var c bytecounter

fmt.fprintf(&c, "hello world")

fmt.println(c) //輸出 11

乙個介面型別定義了一系列方法,如果乙個具體型別要實現該介面,那麼必須實現介面型別定義中的所有方法。另外,我們也可以通過組合已有介面來得到乙個新介面,這樣的語法稱為嵌入式介面,它讓我們可以直接使用乙個介面而不用逐一寫出這個介面所包含的方法。

package io

type reader inte***ce

type closer inte***ce

type readwriter inte***ce

type readwritecloser inte***ce

如果乙個型別實現了介面要求的所有方法,那麼則稱這個型別實現了這個介面。介面的賦值規則也很簡單,僅當乙個表示式實現了乙個介面時,這個表示式才可以賦給該介面。

var w io.writer

w = os.stdout //正確,*os.file有write方法

w = new(bytes.buffer) //正確,*bytes.buffer有write方法

w = time.second //編譯錯誤,time.duration沒有write方法

當右側表示式也是乙個介面時,該規則也有效,此時右側表示式的介面必須實現了左側介面型別的所有方法才被允許賦值。

乙個不包含任何方法的介面稱為空介面。雖然我們無法從空介面中獲得任何資訊,看起來空介面沒有任何用途,但實際上空介面是不可缺少的。正因為空介面型別對其實現型別沒有任何要求,所以我們可以把任何值賦給空介面型別。

var any inte***ce{}

any = true

any = 12.34

any = "hello"

any = map[string]int

當然,即使我們建立了乙個指向布林值、浮點數、字串、map或者其他型別的空介面,也無法直接使用其中的值,畢竟這個介面不包含任何方法。我們需要乙個方法從空介面中還原出實際值,用型別斷言可以做到。 

從概念上講,乙個介面型別的值(介面值):乙個具體型別和該型別的乙個值。二者稱為介面的動態型別和動態值。對於像go這樣的靜態型別語言,型別僅僅是乙個編譯時的概念,所以型別不是乙個值。

在go語言中,變數總是初始化為乙個特定的值,介面也不例外。以下**將說明這一點:

var w io.writer

w = os.stdout

w = new(bytes.buffer)

w = nil

上述**中,第一行宣告了乙個介面型別的變數w,此時w的型別和值都為nil——介面的零值就是把它的動態型別和值都設定為nil。乙個介面值是否為nil取決於它的動態型別,所以現在這是乙個nil介面值,可以與nil相比較。

第二行把乙個具體型別隱式轉換為乙個介面型別,這行**與 w = io.writer(os.stdout) 等價。此時,w的動態型別轉換為*os.file,而其值則會設定為os.stdout的副本,即乙個指向代表程序的標準輸出的os.file型別的指標。執行這行語句後,呼叫該介面值的方法,會實際呼叫(*os.file)的方法,如:w.write等價於(*os.file).write。第三行語句也同理。

第四行語句會將w的型別和值都設定為nil,把w恢復到它宣告時的狀態。

綜上可知,介面值可以互相比較,兩個介面值的型別和值都是可比較的,且型別和值都相等時才相等。

本文部分內容摘自《go程式語言》

Go 語言介面

go 語言提供了另外一種資料型別即介面,它把所有的具有共性的方法定義在一起,任何其他型別只要實現了這些方法就是實現了這個介面。例項 定義介面 type inte ce name inte ce 定義結構體 type struct name struct 實現介面方法 func struct name...

Go語言 介面

介面代表一種呼叫契約,是多個方法宣告的集合。介面要實現的是做什麼,而不關心如何做。介面最常見的使用場景是對包外提供訪問,或預留擴充套件空間。go語言介面實現機制很簡潔,只要目標型別方法集內包含介面宣告的全部方法,就被視為實現了該介面,無需做顯示宣告,當然目標型別可實現多個介面。其實介面也是一中結構,...

Go語言 介面

在go語言中,乙個介面型別總是代表著某一種型別 即所有實現它的型別 的行為。乙個介面型別的宣告通常會包含關鍵字type 型別名稱 關鍵字inte ce以及由花括號包裹的若干方法宣告。示例如下 type animal inte ce注意,介面型別中的方法宣告是普通的方法宣告的簡化形式。它們只包括方法名...