本文记录了我在学习Go指南的方法和接口小节的笔记。
- Go中没有类,但是可以为结构体类型和非结构体类型声明方法。在Go中,接收者的类型定义和方法声明必须在同一包内,不能为内建类型声明方法。
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
type MyFloat float64
func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}
- 指针接收者的方法可以修改接收者指向的值。由于方法经常需要修改它的接收者,指针接收者比值接收者更常用。使用指针接收者的好处有:方法能够修改其接收者指向的值;可以避免在每次调用方法时复制该值,若值的类型为大型结构体时,这样做会更加高效。
- 指针接收者的方法被调用时,接收者既能为值,又能为指针。值接收者的方法被调用时,接收者既能为值,又能为指针。对于指针接收者的方法Scale(),Go 会将语句 v.Scale(5) 解释为 (&v).Scale(5)。对于值接收者的方法Abs(),方法调用 p.Abs() 会被解释为 (*p).Abs()。(注:也就是说,在声明方法的时候需要注意是否要改变接收者的值,但是在使用的时候可以忽略方法的接收者是值还是指针)
var v Vertex
v.Scale(5) // OK
p := &v
p.Scale(10) // OK
v.Abs(5) // OK
p = &v
p.Abs(10) // OK
- 在什么时候会区分值接收者和指针接收者呢?当接收者实现的方法为某个接口的方法的时候。
// 这里定义了一个接口,任何实现了Error()方法的类型都可以看作是这个接口。这时,如果实现Error()方法的为指针类型,则值类型的变量没有实现error接口。
type error interface {
Error() string
}
func (e *MyError) Error() string {
return fmt.Sprintf("at %v, %s",
e.When, e.What)
}
func run() error {
return &MyError{ // 这里要用指针
time.Now(),
"it didn't work",
}
}
- 类型通过实现一个接口的所有方法来实现该接口。接口值可以用作函数的参数或返回值。在内部,接口值可以看做包含值和具体类型的元组:(value, type)。
- 即便接口内的具体值为 nil,方法仍然会被 nil 接收者调用。在一些语言中,这会触发一个空指针异常,但在 Go 中通常会写一些方法来优雅地处理它。
func (t *T) M() {
if t == nil {
fmt.Println("<nil>")
return
}
fmt.Println(t.S)
}
如果for循环range的对象是切片,则 for i := range b{} 中i为index;如果b是有缓冲通道,则i为顺序从通道中取出的值。