介面

Go 語言介面:Duck Typing、方法集合。

Interface

介面是方法集合的藍圖。

  • 指定一組方法,但不實作
  • 不宣告變數
  • 不定義方法
type <interface_name> interface {
    // Method signatures
}

基本範例

type Speaker interface {
    Speak() string
}

type Dog struct {
    Name string
}

func (d Dog) Speak() string {
    return "Woof!"
}

type Cat struct {
    Name string
}

func (c Cat) Speak() string {
    return "Meow!"
}

func MakeSound(s Speaker) {
    fmt.Println(s.Speak())
}

func main() {
    dog := Dog{Name: "Buddy"}
    cat := Cat{Name: "Whiskers"}

    MakeSound(dog) // Woof!
    MakeSound(cat) // Meow!
}

Duck Typing

看行為,不看身分

  1. 只要物件能執行需要的方法/行為,就可以使用
  2. 不需要明確繼承或實作介面
  3. 關注「能做什麼」,而非「是什麼」

這種設計讓程式更靈活,但需要小心處理執行期錯誤。

// 任何有 Write 方法的類型都滿足 io.Writer
type Writer interface {
    Write(p []byte) (n int, err error)
}

// 自訂類型
type ConsoleWriter struct{}

func (cw ConsoleWriter) Write(p []byte) (n int, err error) {
    return fmt.Println(string(p))
}

// 可以當作 io.Writer 使用
var w io.Writer = ConsoleWriter{}

Empty Interface

空介面 interface{} 可以持有任何型別的值。

func PrintAnything(v interface{}) {
    fmt.Printf("Type: %T, Value: %v\n", v, v)
}

func main() {
    PrintAnything(42)
    PrintAnything("hello")
    PrintAnything(true)
    PrintAnything([]int{1, 2, 3})
}
Go 1.18+ 可以使用 any 作為 interface{} 的別名。

Type Assertion

型別斷言用於從介面取得具體型別。

var i interface{} = "hello"

// 基本斷言(失敗會 panic)
s := i.(string)

// 安全斷言(推薦)
s, ok := i.(string)
if ok {
    fmt.Println("String:", s)
}

// 斷言失敗不會 panic
n, ok := i.(int)
if !ok {
    fmt.Println("Not an int")
}

Type Switch

用於處理多種可能的型別。

func describe(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Printf("Integer: %d\n", v*2)
    case string:
        fmt.Printf("String: %q (len=%d)\n", v, len(v))
    case bool:
        fmt.Printf("Boolean: %t\n", v)
    case []int:
        fmt.Printf("Slice of int: %v\n", v)
    default:
        fmt.Printf("Unknown type: %T\n", v)
    }
}

Interface Composition

介面可以組合其他介面。

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

// 組合介面
type ReadWriter interface {
    Reader
    Writer
}

Type List

Ordered Type:也支援 <, <=, >, >= 運算符。

type Ordered interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64 |
    ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
    ~float32 | ~float64 |
    ~string
}

func Min[T Ordered](a, b T) T {
    if a < b {
        return a
    }
    return b
}

常見介面

Stringer

type Stringer interface {
    String() string
}

type Person struct {
    Name string
    Age  int
}

func (p Person) String() string {
    return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}

Error

type error interface {
    Error() string
}

type MyError struct {
    Code    int
    Message string
}

func (e MyError) Error() string {
    return fmt.Sprintf("Error %d: %s", e.Code, e.Message)
}

nil

在 error 位置回傳 nil 表示沒有錯誤。

func doSomething() error {
    // 成功時回傳 nil
    return nil
}

// 介面值為 nil 的情況
var s Speaker // nil
if s == nil {
    fmt.Println("s is nil")
}
介面值包含型別和值兩部分。只有當兩者都是 nil 時,介面值才等於 nil。
var p *Dog = nil
var s Speaker = p

// s != nil(型別不是 nil)
if s == nil {
    fmt.Println("this won't print")
}

相關主題