写在前面
Method
- 关于Method,它其实和function差不多
- 如果学过c++或者java,应该接触过类中的成员函数,还有那些需要注意的访问限制(private,protected,public)
- Method有点像类成员函数,但又有区别
- 官方原话: you can define methods on types
- 个人认为method是围绕类型而定义的
- 且看例子:
package main
import "fmt"
type Cube struct {
X,Y,Z float64
}
//以下是method
func (c Cube) volume() float64 {
return c.X*c.Y*c.Z
}
func main() {
c := Cube{2, 3, 4}
fmt.Println(c.volume())
}
- 上述例子中,先定义了一个结构体类型Cube,然后定义的method:volume就是围绕Cube类型展开的
- 和一般的function差不多,func关键字;function的定义是先名字的,而method是先“参数“,这里的”参数“,就是receiver参数
- method是围绕receiver参数来展开的,这就有点像是receiver的“成员函数”了
- 定义好了receiver参数后才是method的名字,返回值类型,然后是具体的操作
- method的使用方法:定义一个receiver类型的变量,由该变量调用相关的method
- 官方原话:a method is just a function with a receiver argument.
- 我个人倾向于认为它是receiver类型的成员函数(目前是这样,因为了解不够多)
- 关于receiver的类型,是有限制的
- 说到这个问题,有必要再重申一下,在官方教程的开始,有这么一句话:Every Go program is made up of packages.
- 而receiver的类型,不能是其他packages中定义好的,例如你不能用int作为receiver的类型,因为这是内置的类型
package main
import "fmt"
type MyInt int
func (i MyInt) increase() int {
return int(i) //这里一定要进行类型转换,和函数返回类型一致,就算是别名,在编译器里也认为不同
}
func main() {
i := MyInt(2)
fmt.Println(i.increase())
}
- receiver还可以是指针类型
- 但同样也不能是其他packages定义号的,例如*int作为receiver的类型是不允许的
- 这部分,建议不懂的读者可以看看C++引用和指针的内容
package main
import "fmt"
type Cube struct {
X, Y, Z float64
}
func (c Cube) volume() float64 {
return c.X*c.Y*c.Z //这里不用转换,因为Cube里的X,Y,Z都是float64类型
}
//以下就是指针类型的receiver(pointer receiver),相当于引用,会改变调用它的receiver的内容
//如果不用指针类型(也就是用value receiver),则改变的值只是receiver的copy,对调用它的receiver没有任何改变
func (c *Cube) Scale(f float64) {
c.X = c.X * f
c.Y = c.Y * f
c.Z = c.Z * f
}
func main() {
c := Cube{2, 3, 4} //value receiver
p := &Cube{2, 3, 4} //pointer receiver
//使用value receiver变量来调用pointer receiver变量的Scale方法
//这时,编译器会自动处理成(&c).Scale(n)
//n指的是参数,在这里的例子为2
c.Scale(2)
p.Scale(2)
//使用pointer receiver变量来调用value receiver变量的volume方法
//这时,编译器会处理成(*p).volume()
fmt.Println(c.volume())
fmt.Println(p.volume())
}
- 请读者仔细观察上述代码和注释,能发现method的使用,简化了value receiver和pointer reciver
- 但是,对于一般的function,是不作这种类型的简化的
- 那么用value receiver还是pointer receiver呢?
- 官方推荐用pointer receiver,原因有二
- 可以改变receiver的值
- 对于较大的数据结构,引用效率高
interface
- interface有点像c++的抽象类,而interface里的成员就像c++的纯虚函数
- 而实现interface的成员,是通过method中的receiver
- interface并不能像method一样,自动识别pointer receiver或value receiver
package main
import (
"fmt"
"math"
)
type OneAdder interface {
OneAdd() int
}
type MyInt int
func (i MyInt) OneAdd() int {
return int(i+1) //类型转换
}
type Suber interface {
Sub() float64
}
type MyFloat float64
func (f *MyFloat) Sub() float64 {
return float64((*f) - 1) //类型转换
}
func main() {
var a OneAdder
i := MyInt(1)
a = i
fmt.Println(a.OneAdd())
var b Suber
/*
这种写法会报错
j := &MyFloat(math.Pi)
b = j
*/
j := MyFloat(math.Pi)
b = &j
fmt.Println(b.Sub())
}
package main
import (
"fmt"
"math"
)
type Adder interface {
Add() float64
}
type MyFloat float64
func (f MyFloat) Add() float64 {
return float64(f+1)
}
type Volumer interface {
Volume() float64
}
type Cube struct {
X,Y,Z float64
}
func (c Cube) Volume() float64 {
return c.X*c.Y*c.Z
}
func main() {
var a Adder = MyFloat(math.Pi)
fmt.Println(a.Add())
var v Volumer = Cube{math.Pi, 1, 1}
fmt.Println(v.Volume())
}
- 当然,pointer receiver也是允许隐式实现接口的
- 但是,有些情况下不行,注意示例
package main
import (
"fmt"
"math"
)
type Adder interface {
Add() float64
}
type MyFloat float64
//这里改成了pointer receiver
func (f *MyFloat) Add() float64 {
return float64((*f)+1)
}
type Volumer interface {
Volume() float64
}
type Cube struct {
X,Y,Z float64
}
func (c *Cube) Volume() float64 {
return c.X*c.Y*c.Z
}
func main() {
// 特殊情况,要设定中间变量
var a Adder
p := MyFloat(math.Pi) //注意到,这里是类型转换
a = &p
fmt.Println(a.Add())
// 一般情况
var v Volumer = &Cube{math.Pi, 1, 1} //注意到,这里是初始化
fmt.Println(v.Volume())
}
- interface value相当于一个元组(value, type)
- 当receiver没有初始化为某个值时,将为nil
- 当interface value没有初始化为某个值时,元组会变为(nil, nil),这时候还会报错
- 当interface内没有任何成员时,元组会变为(nil, nil),但是不会报错