【Day 5】Golang语法学习 ---2021/12/31

选择值或指针作为接收者

使用指针接收者的原因有二:

首先,方法能够修改其接收者指向的值。

其次,这样可以避免在每次调用方法时复制该值。若值的类型为大型结构体时,这样做会更加高效。

在本例中,ScaleAbs 接收者的类型为 *Vertex,即便 Abs 并不需要修改其接收者。

通常来说,所有给定类型的方法都应该有值或指针接收者,但并不应该二者混用。(我们会在接下来几页中明白为什么。)

methods-with-pointer-receivers.go

package main

import (
	"fmt"
	"math"
)

type Vertex struct {
	X, Y float64
}

func (v *Vertex) Scale(f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}

func (v *Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
	v := &Vertex{3, 4}
	fmt.Printf("Before scaling: %+v, Abs: %v\n", v, v.Abs())
	v.Scale(5)
	fmt.Printf("After scaling: %+v, Abs: %v\n", v, v.Abs())
}

结果:

Before scaling: &{X:3 Y:4}, Abs: 5
After scaling: &{X:15 Y:20}, Abs: 25

接口

接口类型 是由一组方法签名定义的集合。

接口类型的变量可以保存任何实现了这些方法的值。

注意: 示例代码的 22 行存在一个错误。由于 Abs 方法只为 *Vertex (指针类型)定义,因此 Vertex(值类型)并未实现 Abser

interfaces.go

package main

import (
	"fmt"
	"math"
)

type Abser interface {
	Abs() float64
}

func main() {
	var a Abser
	f := MyFloat(-math.Sqrt2)
	v := Vertex{3, 4}

	a = f  // a MyFloat 实现了 Abser
	a = &v // a *Vertex 实现了 Abser

	// 下面一行,v 是一个 Vertex(而不是 *Vertex)
	// 所以没有实现 Abser。
	a = v

	fmt.Println(a.Abs())
}

type MyFloat float64

func (f MyFloat) Abs() float64 {
	if f < 0 {
		return float64(-f)
	}
	return float64(f)
}

type Vertex struct {
	X, Y float64
}

func (v *Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

结果:

5

接口与隐式实现

类型通过实现一个接口的所有方法来实现该接口。既然无需专门显式声明,也就没有“implements”关键字。

隐式接口从接口的实现中解耦了定义,这样接口的实现可以出现在任何包中,无需提前准备。

因此,也就无需在每一个实现上增加新的接口名称,这样同时也鼓励了明确的接口定义。

interfaces-are-satisfied-implicitly.go

package main

import "fmt"

type I interface {
	M()
}

type T struct {
	S string
}

// 此方法表示类型 T 实现了接口 I,但我们无需显式声明此事。
func (t T) M() {
	fmt.Println(t.S)
}

func main() {
	var i I = T{"hello"}
	i.M()
}

结果:

Hello

上一篇:Android:SurfaceView忽略了postInvalidate()?


下一篇:Spring | IOC及 Spring IOC 的具体表现形式