方法
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。