# 11.6 使用方法集與接口

在第 10.6.3 節及例子 methodset1.go 中我們看到,作用於變量上的方法實際上是不區分變量到底是指針還是值的。當碰到接口類型值時,這會變得有點複雜,原因是接口變量中存儲的具體值是不可尋址的,幸運的是,如果使用不當編譯器會給出錯誤。考慮下面的程序:

示例 11.5 methodset2.go

package main

import (
    "fmt"
)

type List []int

func (l List) Len() int {
    return len(l)
}

func (l *List) Append(val int) {
    *l = append(*l, val)
}

type Appender interface {
    Append(int)
}

func CountInto(a Appender, start, end int) {
    for i := start; i <= end; i++ {
        a.Append(i)
    }
}

type Lener interface {
    Len() int
}

func LongEnough(l Lener) bool {
    return l.Len()*10 > 42
}

func main() {
    // A bare value
    var lst List
    // compiler error:
    // cannot use lst (type List) as type Appender in argument to CountInto:
    //       List does not implement Appender (Append method has pointer receiver)
    // CountInto(lst, 1, 10)
    if LongEnough(lst) { // VALID:Identical receiver type
        fmt.Printf("- lst is long enough\n")
    }

    // A pointer value
    plst := new(List)
    CountInto(plst, 1, 10) //VALID:Identical receiver type
    if LongEnough(plst) {
        // VALID: a *List can be dereferenced for the receiver
        fmt.Printf("- plst is long enough\n")
    }
}

討論

lst 上調用 CountInto 時會導致一個編譯器錯誤,因爲 CountInto 需要一個 Appender,而它的方法 Append 只定義在指針上。 在 lst 上調用 LongEnough 是可以的因爲 'Len' 定義在值上。

plst 上調用 CountInto 是可以的,因爲 CountInto 需要一個 Appender,並且它的方法 Append 定義在指針上。 在 plst 上調用 LongEnough 也是可以的,因爲指針會被自動解引用。

總結

在接口上調用方法時,必須有和方法定義時相同的接收者類型或者是可以從具體類型 P 直接可以辨識的:

  • 指針方法可以通過指針調用
  • 值方法可以通過值調用
  • 接收者是值的方法可以通過指針調用,因爲指針會首先被解引用
  • 接收者是指針的方法不可以通過值調用,因爲存儲在接口中的值沒有地址

將一個值賦值給一個接口賦值時,編譯器會確保所有可能的接口方法都可以在此值上被調用,因此不正確的賦值在編譯期就會失敗。

譯註

Go 語言規範定義了接口方法集的調用規則:

  • 類型 T 的可調用方法集包含接受者爲 T 或 T 的所有方法集
  • 類型 T 的可調用方法集包含接受者爲 T 的所有方法
  • 類型 T 的可調用方法集不包含接受者爲 *T 的方法

鏈接

results matching ""

    No results matching ""