1 package main
 2 import (
 3    "fmt"
 4    "math"
 5 )
 6 
 7 type Vertex struct {
 8    X, Y float64
 9 }
10 func (v Vertex) Abs() float64 {//abs()函数有一个vertex类型的接收者
11    return math.Sqrt(v.X*v.X + v.Y*v.Y)//3*3+4*4=25,25开方=5
12 }
13 func main() { 14 v := Vertex{3, 4}//传参 15 fmt.Println(v.Abs()) 16 }

 

 

 

 

 

方法即函数

记住:方法只是个带接收者参数的函数。
现在这个 Abs 的写法就是个正常的函数,功能并没有什么变化。
 
 1 type Vertex struct {
 2     X, Y float64
 3 }
 4 
 5 
 6 func Abs(v Vertex) float64 {
 7     return math.Sqrt(v.X*v.X + v.Y*v.Y)
 8 }
 9 
10 
11 func main() {
12     v := Vertex{3, 4}
13     fmt.Println(Abs(v))
14 }

 

你也可以为非结构体类型声明方法。
在此例中,我们看到了一个带 Abs 方法的数值类型 MyFloat。
你只能为在同一包内定义的类型的接收者声明方法,而不能为其它包内定义的类型(包括 int 之类的内建类型)的接收者声明方法。
 
(译注:就是接收者的类型定义和方法声明必须在同一包内;不能为内建类型声明方法。)

 

 1 package main
 2 import (
 3    "fmt"
 4    "math"
 5 )
 6 
 7 type MyFloat float64
 8 //接收者的类型定义和方法声明在同一包内
 9 
10 func (f MyFloat) Abs() float64 {//转换符号,将负数转成正数
11    if f < 0 {
12       return float64(-f)
13    }
14    return float64(f)
15 }
16 
17 func main() {
18    f := MyFloat(-math.Sqrt2)
19    fmt.Println(-math.Sqrt2)
20    fmt.Println(f)
21    fmt.Println(f.Abs())
22 }

 

 

指针接收者

你可以为指针接收者声明方法。
这意味着对于某类型 T,接收者的类型可以用 *T 的文法。(此外,T 不能是像 *int 这样的指针。)[不能是指针的指针]
例如,这里为 *Vertex 定义了 Scale 方法。
指针接收者的方法可以修改接收者指向的值(就像 Scale 在这做的)。由于方法经常需要修改它的接收者,指针接收者比值接收者更常用。
 
试着移除第 16 行 Scale 函数声明中的 *,观察此程序的行为如何变化。
若使用值接收者,那么 Scale 方法会对原始 Vertex 值的副本进行操作。(对于函数的其它参数也是如此。)Scale 方法必须用指针接受者来更改 main 函数中声明的 Vertex 的值。

 

 1 package main
 2 
 3 
 4 import (
 5     "fmt"
 6     "math"
 7 )
 8 
 9 
10 type Vertex struct {
11     X, Y float64
12 }
13 
14 
15 func (v Vertex) Abs() float64 {
16     return math.Sqrt(v.X*v.X + v.Y*v.Y)
17 }
18 
19 
20 func (v *Vertex) Scale(f float64) {
21 //当给*Vertex定义scale方法,运行时为v的x为30,y为40v.abs()为30*30—+40*40=2500的开方为50
22 //当给Vertex定义scale方法,运行scale时操作的是vertex的副本,不改变原始值。运行v.abs()时v的x为x仍为3,y仍为4,因此开方得5
23     v.X = v.X * f
24     v.Y = v.Y * f
25 }
26 
27 
28 func main() {
29     v := Vertex{3, 4}
30     v.Scale(10)
31     fmt.Println(v.Abs())
32 }

 

 

指针与函数

现在我们要把 Abs 和 Scale 方法重写为函数。
同样,我们先试着移除掉第 16 的 *。你能看出为什么程序的行为改变了吗?要怎样做才能让该示例顺利通过编译?
 
 1 package main
 2 
 3 
 4 import (
 5     "fmt"
 6     "math"
 7 )
 8 
 9 
10 type Vertex struct {
11     X, Y float64
12 }
13 
14 
15 func Abs(v Vertex) float64 {
16     return math.Sqrt(v.X*v.X + v.Y*v.Y)
17 }
18 
19 
20 func Scale(v *Vertex, f float64) {
21     v.X = v.X * f
22     v.Y = v.Y * f
23 }
24 
25 
26 func main() {
27     v := Vertex{3, 4}
28     Scale(&v, 10)
29     fmt.Println(Abs(v))
30 }

 

【移除*后报错如下,去掉主函数的scale函数调用时的传参(&v,10)中的&编译通过】
【可知,方法和参数要对应好】

 

 

方法与指针重定向

【方法和函数,最显见的区别是调用时的文法不同】
 1 func (v *Vertex) Scale(f float64) {
 2 //当接收者是指针时的方法被调用时,无论是值还是指针都能调用。如下main函数中的v为值可以调用scale方法,而p为指针也能直接调用scale方法。Go 会将语句 v.Scale(5) 解释为 (&v).Scale(5)
 3     v.X = v.X * f
 4     v.Y = v.Y * f
 5 }
 6 
 7 
 8 func ScaleFunc(v *Vertex, f float64) {
 9 //带指针参数的函数必须传入的是指针类型的参数,如下main函数中的v是vertex类型,在调用此函数时传入的是(&v,1010 //而非指针调用函数时传入须传入指针
11     v.X = v.X * f
12     v.Y = v.Y * f
13 }
14 
15 
16 func main() {
17     v := Vertex{3, 4}
18     v.Scale(2)
19     ScaleFunc(&v, 10)
20 
21 
22     p := &Vertex{4, 3}
23     p.Scale(3)//方法的文法
24     ScaleFunc(p, 8)//函数的文法
25 
26 
27     fmt.Println(v, p)
28 }

函数调用接收传参时也是诸如此类

 1 func (v Vertex) Abs() float64 {//接收者为vertex类型的方法
 2     return math.Sqrt(v.X*v.X + v.Y*v.Y)
 3 }
 4 
 5 
 6 func AbsFunc(v Vertex) float64 {//传入的为vertex类型的值
 7     return math.Sqrt(v.X*v.X + v.Y*v.Y)
 8 }
 9 
10 
11 func main() {
12     v := Vertex{3, 4}
13     fmt.Println(v.Abs())
14     fmt.Println(AbsFunc(v))
15 
16 
17     p := &Vertex{4, 3}
18     fmt.Println(p.Abs())//指针类型也可直接调用abs()方法
19     fmt.Println(AbsFunc(*p))//指针类型要转化为值传参
20 }

 

 

选择值或指针作为接收者

使用指针接收者的原因有二:
首先,方法能够修改其接收者指向的值。
其次,这样可以避免在每次调用方法时复制该值。若值的类型为大型结构体时,这样做会更加高效。
在本例中,Scale 和 Abs 接收者的类型为 *Vertex,即便 Abs 并不需要修改其接收者。
通常来说,所有给定类型的方法都应该有值或指针接收者,但并不应该二者混用。(我们会在接下来几页中明白为什么。)
 
 1 func (v *Vertex) Scale(f float64) {
 2    v.X = v.X * f
 3    v.Y = v.Y * f
 4 }
 5 
 6 
 7 func (v *Vertex) Abs() float64 {
 8    return math.Sqrt(v.X*v.X + v.Y*v.Y)
 9 }
10 
11 
12 func main() {
13    v := &Vertex{3, 4}
14    fmt.Printf("Before scaling: %+v, Abs: %v\n", v, v.Abs())//3*3+4*4=25开方=5
15    v.Scale(5)//3*5=15,4*5=20=35
16    fmt.Printf("After scaling: %+v, Abs: %v\n", v, v.Abs())//15平方+20平方的开方得25
17 }

 

 

 

 

 

 

 

 

 

 

 

posted on 2022-08-02 18:53  Jolyne123  阅读(27)  评论(0)    收藏  举报