《Go语音圣经(中文版)》笔记(18)
#《Go 语音圣经(中文版)》
#Panic 异常
#Recover 捕获异常
看不懂。。待看。[1]
#方法
#方法声明
在函数声明时,在其名字之前放上一个变量,即是一个方法。这个附加的参数会将该函数附加到这种类型上,即相当于为这种类型定义了一个独占的方法。
1 | package main |
上面的代码里那个附加的参数p,叫做方法的接收器(receiver)。
1 | [18:59:54] onns@Onns ~/Onns/code/go/go-bible/ch6 $ go run geometry/geometry.go |
在Go语言中可以任意的选择接收器的名字。由于接收器的名字经常会被使用到,建议是可以使用其类型的第一个字母。
在方法调用过程中,接收器参数一般会在方法名之前出现。
第一个Distance的调用实际上用的是包级别的函数geometry.Distance,而第二个则是使用刚刚声明的Point,调用的是Point类下声明的Point.Distance方法。
这种p.Distance的表达式叫做选择器,因为他会选择合适的对应p这个对象的Distance方法来执行。选择器也会被用来选择一个struct类型的字段,比如p.X。由于方法和字段都是在同一命名空间,所以如果我们在这里声明一个X方法的话,编译器会报错,因为在调用p.X时会有歧义。
1 | // A Path is a journey connecting the points with straight lines. |
我们可以给同一个包内的任意命名类型定义方法,只要这个命名类型的底层类型不是指针或者interface。
对于一个给定的类型,其内部的方法都必须有唯一的方法名,但是不同的类型却可以有同样的方法名。
#基于指针对象的方法
1 | func (p *Point) ScaleBy(factor float64) { |
这个方法的名字是(*Point).ScaleBy,这里的括号是必须的,没有括号的话这个表达式可能会被理解为*(Point.ScaleBy)。
在现实的程序里,一般会约定如果Point这个类有一个指针作为接收器的方法,那么所有Point的方法都必须有一个指针接收器,即使是那些并不需要这个指针接收器的函数。
只有类型Point和指向他们的指针(*Point),才可能是出现在接收器声明里的两种接收器。
为了避免歧义,在声明方法时,如果一个类型名本身是一个指针的话,是不允许其出现在接收器中的:
1 | type P *int |
如果接收器p是一个Point类型的变量,并且其方法需要一个Point指针作为接收器,我们可以用下面这种简短的写法:
1 | p.ScaleBy(2) |
编译器会隐式地帮我们用&p去调用ScaleBy这个方法。
不能通过一个无法取到地址的接收器来调用指针方法,比如临时变量的内存地址就无法获取得到:
1 | Point{1, 2}.ScaleBy(2) // compile error: can't take address of Point literal |
不管你的 method 的 receiver 是指针类型还是非指针类型,都是可以通过指针/非指针类型进行调用的,编译器会帮你做类型转换。
在声明一个 method 的 receiver 该是指针还是非指针类型时,你需要考虑两方面的因素,第一方面是这个对象本身是不是特别大,如果声明为非指针变量时,调用会产生一次拷贝;第二方面是如果你用指针类型作为 receiver,那么你一定要注意,这种指针类型指向的始终是一块内存地址,就算你对其进行了拷贝。