Go語言學習筆記(六)-方法和接口

方法

Go中沒有類,但是可以為結構體定義方法,方法就是一類帶有特殊的接受者參數的函數。方法接受者在它自己的參數列表內,位于func關鍵字和方法名之間。例如:

package main
import "fmt"
type Vertex struct{
    x,y float64
}
func (v Vertex) Abs() float64{
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
    v := Vertex{3, 4}
    fmt.Println(v.Abs())
}

你也可以為非結構體類型聲明方法。但只能為在同一包內定義的類型的接收者聲明方法, 而不能為其它包內定義的類型(包括 int 之類的內建類型)的接收者聲明方法。也就是說接收者的定義與方法的聲明必須在同一包內,且不能為內建類型聲明方法。

指針接收者

在Go中可以為指針接收者定義方法,對于某個類型T的接收者的類型可以使用T文法(T不能是像int之類的指針)。指針接收者的方法可以修改接收者指向的值。由于方法經常需要修改它的接收者,指針接收者比值接收者更常用。
帶指針參數的函數必須接受一個指針,而以指針為接收者的方法被調用時,接收者既能為值又能為指針。接受一個值作為參數的函數必須接受一個指定類型的值,而以值為接收者的方法被調用時,接收者既能為值又能為指針。
在開發中建議選擇指針作為接收者,這樣做有兩個好處:

  • 方法可以直接修改接收者的值
  • 這樣可以避免在每次調用方法時復制該值。若值的類型為大型結構體時,這樣做會更加高效

接口

接口是由一組方法簽名定義的集合,接口類型的值可以保存任何實現了接口方法的變量。類型通過實現了一個接口的所有方法來實現這個接口,而不需要專門的顯示聲明也就是"implements"關鍵字來聲明。隱式接口從接口的實現中解耦了定義,這樣接口的實現可以出現在任何包中,無需提前準備。
在內部,接口的值可以看做是包含值和具體類型的元組:

( value , type )

接口的值保存了一個具體底層類型的具體值,接口值調用方法時會調用具體底層類型的同名方法。在Go中即使接口值的底層值是nil,方法仍然會被nil的接收者調用。

package main

import "fmt"

type I interface {
    M()
}

type T struct {
    S string
}

func (t *T) M() {
    if t == nil {
        fmt.Println("<nil>")
        return
    }
    fmt.Println(t.S)
}

func main() {
    var i I

    var t *T
    i = t
    describe(i)
    i.M()

    i = &T{"hello"}
    describe(i)
    i.M()
}

func describe(i I) {
    fmt.Printf("(%v, %T)\n", i, i)
}

//輸出結果
//(<nil>, *main.T)
//<nil>
//(&{hello}, *main.T)
//hello

而nil接口值不保存值也不保存具體類型。為nil接口調用方法會產生運行時錯誤,因為接口的元組內并未包含能夠指明該調用哪個 具體 方法的類型。
指定了零個方法的接口被稱為空接口,即:

interface{}

空接口可以保存任意類型的值,空接口一般是用來處理位置類型的值。

類型斷言提供了訪問接口值底層具體值的方法。

//斷言i是T類型的值,并返回i的底層值返回給t
t := i.(T)

//斷言i是V類型的值,若i是V類型的值ok的為true,v為i的底層值,否則ok為false,v為零值。
v,ok := i.(V)

以上就是接口斷言的兩種方式,第一種會觸發warning提示,第二種則不會觸發。因為第二種方式判斷了i是否保存了T類型的值。

類型選擇是一種按順序從幾個類型語言中選擇分支的結構。與switch相似,不同的是類型選擇中的case為類型。聲明與類型斷言相似,但是括號內由具體類型修改為type關鍵字。

package main

import "fmt"

func do(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Printf("Twice %v is %v\n", v, v*2)
    case string:
        fmt.Printf("%q is %v bytes long\n", v, len(v))
    default:
        fmt.Printf("I don't know about type %T!\n", v)
    }
}

func main() {
    do(21)
    do("hello")
    do(true)
}

Stringer

fmt包中定義的Stringer是最普遍的接口之一。

type Stringer interface { String() string}

Stringer是一個可以用字符串描述自己的類型。fmt包(還有很多包)都通過此接口來打印值,Stringer的功能有點類似于java中的toString。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容