在最簡單的形式裡,介面型別是一組方法簽章。
以下是一個介面定義的範例,其中包含 Add 和 Value 這兩個方法:
type Counter interface {
Add(increment int)
Value() int
}
像 increment 這樣的參數名稱可以從介面定義中省略,不過它們通常能提升可讀性。
Go 的介面名稱不包含 Interface 或 I 這個字。
它們通常以 er 結尾,例如 Reader、Stringer。
任何定義了介面方法的型別,都會自動隱含地「實作」該介面。
Go 裡沒有 implements 關鍵字。
以下型別實作了我們上面看到的 Counter 介面。
type Stats struct {
value int
// ...
}
func (s Stats) Add(v int) {
s.value += v
}
func (s Stats) Value() int {
return s.value
}
func (s Stats) SomeOtherMethod() {
// The type can have additional methods not mentioned in the interface.
}
就實作介面而言,方法擁有的是值接收者還是指標接收者並不重要。 (如果你對這些還不熟,可以回頭看看方法的概念。)
介面型別的值可以保存任何有實作這些方法的值。1
這表示 Stats 現在可以用在所有需要 Counter 介面的地方。
func SetUpAnalytics(counter Counter) {
// ...
}
stats := Stats{}
SetUpAnalytics(stats)
// works because Stats implements Counter
如果函式的所有參數和回傳值都必須使用具體(也就是非介面)型別,會嚴重限制它們的重用性。 只要讓參數改用介面型別,就只會定義函式完成工作所需的行為。 這麼一來,函式就能搭配不同具體型別的參數使用,只要那些具體型別有實作這個介面就好。 同樣的邏輯也適用於以介面型別作為回傳值的情況。
因為介面是隱含實作的,所以一個型別可以輕鬆實作多個介面。 它只需要定義好所有必要的方法。
介面定義可以包含其他介面。 以下是標準函式庫裡的一個範例。
type ReadCloser interface {
Reader
Closer
}
// Based on these interfaces:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
定義 ReadCloser 時,介面定義沒有重複寫出 Read 和 Close 的方法簽章,而是沿用了 Reader 和 Closer 現有的定義。
Go 裡有一個非常特別的介面型別,也就是不含任何方法的空介面。
空介面寫成這樣:interface{}。
在 Go 1.18 或更高的版本中,也可以使用 any。
它被定義為別名。
由於空介面沒有任何方法,因此每個型別都隱含地實作了它。 這在定義能通用地接受任何值的函式時很有幫助。 在這種情況下,函式參數會使用空介面型別。
例如:
func AnythingGoes(i interface{}) string {
if i == 0 {
return "zero"
}
if i == "" {
return "empty string"
}
return "something else"
}