什麼是接口?

在 Go 中,接口是一種類型,它定義了一組方法簽名(即方法名、參數和返回值),但不包含這些方法的具體實現。

換句話説,接口描述了“一個類型能做什麼”,而不是“它是什麼”。

接口的定義

使用 interface 關鍵字定義接口:

type Speaker interface {
    Speak() string
}

這個 Speaker 接口規定:任何實現了 Speak() string 方法的類型,都自動滿足(implement)該接口。

隱式實現:Go 的優雅之處

與其他語言(如 Java、C#)不同,Go 中不需要顯式聲明某個類型實現了某個接口。只要你的類型擁有接口中定義的所有方法,它就“自動”實現了該接口。

來看一個例子:

package main

import "fmt"

// 定義接口
type Speaker interface {
    Speak() string
}

// 定義兩個結構體
type Dog struct {
    Name string
}

type Cat struct {
    Name string
}

// 為 Dog 實現 Speak 方法
func (d Dog) Speak() string {
    return d.Name + " says: Woof!"
}

// 為 Cat 實現 Speak 方法
func (c Cat) Speak() string {
    return c.Name + " says: Meow!"
}

func main() {
    var s Speaker

    s = Dog{Name: "Buddy"}
    fmt.Println(s.Speak()) // Buddy says: Woof!

    s = Cat{Name: "Mimi"}
    fmt.Println(s.Speak()) // Mimi says: Meow!
}

在這個例子中:

  • Dog 和 Cat 並沒有聲明“我實現了 Speaker 接口”;
  • 但因為它們都有 Speak() string 方法,所以它們自動滿足 Speaker 接口;
  • 因此可以賦值給 Speaker 類型的變量。

這種“鴨子類型”(Duck Typing)的設計,讓 Go 的代碼更加靈活、解耦。

空接口:interface{}

Go 中有一個特殊的接口叫 空接口

interface{}

它不包含任何方法,因此所有類型都實現了空接口。這使得 interface{} 可以接收任意類型的值,類似於其他語言中的 “any” 或 “Object”。

func PrintAnything(v interface{}) {
    fmt.Println(v)
}

PrintAnything(42)           // int
PrintAnything("hello")      // string
PrintAnything([]int{1,2,3}) // slice

⚠️ 注意:雖然空接口很靈活,但過度使用會削弱類型安全。在 Go 1.18 引入泛型後,很多原本用 interface{} 的場景現在可以用泛型替代。

接口的用途

  1. 多態性:同一接口變量可指向不同類型的實例,調用各自的方法。
  2. 解耦:函數依賴接口而非具體類型,便於測試和擴展。
  3. 標準庫廣泛使用:如 io.Readerio.Writererror 等都是接口。

例如,error 接口定義如下:

type error interface {
    Error() string
}

只要你自定義的類型實現了 Error() string,就可以作為錯誤返回!