Go 语言中同时有函数和方法。一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。所有给定类型的方法属于该类型的方法集。语法格式如下:
func (variable_name variable_data_type) function_name() [return_type]{
/* 函数体*/
}
方法是特殊的函数,定义在某一特定的类型上,通过类型的实例来进行调用,这个实例被叫接收者(receiver)。
注意: Go语言不允许为简单的内置类型添加方法,所以下面定义的方法是非法的。
package main
import(
"fmt"
)
func Add(a ,b int){ //函数合法
fmt.Println(a+b)
}
func (a int) Add (b int){ //方法非法!不能是内置数据类型
fmt.Println(a+b)
}
合法的方法定义如下:
package main
import(
"fmt"
)
type myInt int
func Add(a ,b int){ //函数
fmt.Println(a+b)
}
func (a myInt) Add (b myInt){ //方法
fmt.Println(a+b)
}
func main() {
a, b := 3,4
var aa,bb myInt = 3,4
Add(a,b)
aa.Add(bb)
}
上面的表达式aa.Add称作选择子(selector),它为接收者aa选择合适的Add方法。
receiver也是参数,涉及到指针传递还是值的传递
当调用一个函数时,会对其每一个参数值进行拷贝,如果一个函数需要更新一个变量,或者函数的其中一个参数实在太大我们希望能够避免进行这种默认的拷贝,这种情况下我们就需要用到指针了。对应到我们这里用来更新接收器的对象的方法,当这个接受者变量本身比较大时,我们就可以用其指针而不是对象来声明方法,,如下:
func (p *Point) ScaleBy(factor float64) {
p.X *= factor
p.Y *= factor
}
想要调用指针类型方法(*Point).ScaleBy,只要提供一个Point类型的指针即可,像下面这样。
r := &Point{1, 2}
r.ScaleBy(2)
fmt.Println(*r) // "{2, 4}"
或者这样:
p := Point{1, 2}
pptr := &p
pptr.ScaleBy(2)
fmt.Println(p) // "{2, 4}"
或者这样:
p := Point{1, 2}
(&p).ScaleBy(2)
fmt.Println(p) // "{2, 4}"
不过后面两种方法有些笨拙。幸运的是,go语言本身在这种地方会帮到我们。如果接收器p是一个Point类型的变量,并且其方法需要一个Point指针作为接收器,我们可以用下面这种简短的写法:
p.ScaleBy(2)
编译器会隐式地帮我们用&p去调用ScaleBy这个方法。这种简写方法只适用于“变量”,包括struct里的字段比如p.X,以及array和slice内的元素比如perim[0]。
package main
import (
"fmt"
)
type A struct {
Name string
}
type B struct {
Name string
}
func main() {
a := A{}
a.Print()
fmt.Println(a.Name)
b := B{}
b.Print()
fmt.Println(b.Name)
}
//编译器根据接收者的类型,来判断它是属于哪个方法
func (a *A) Print() { //加上*代表指针传递
//取一个变量a,a就是接收者,它的接收者的类型就是structA,Print就是方法的名称,参数在Print()的括号中定义
//receiver就是这个函数的第一个接收者,而且是强制规定的,这个时候就变成了一个方法
a.Name = "AA"
fmt.Println("A")
}
func (b B) Print() { //这里的b并不是以指针传递
b.Name = "BB"
fmt.Println("B")
}
运行结果是:
A
AA
B
结果所示:值类型不使用指针,在这个方法结束之后,值不会被修改
方法值和方法表达式
package main
import (
"fmt"
)
type TZ int
func main() {
var a TZ
a.Print() //Method Value
(*TZ).Print(&a) //Method Expression 这是两种不同的调用方法
}
func (a *TZ) Print() {
fmt.Println("TZ")
}
方法访问权限
package main
import (
"fmt"
)
type A struct {
mm string //首字母小写代表私有字段
//首字母的大小写还是以包为级别来界定它的公有还是私有
}
func main() {
a := A{}
a.Print()
fmt.Println(a.mm)
}
func (a *A) Print() {
a.mm = "123"
fmt.Println(a.mm)
}
运行结果是:
123
123
打印结果证明方法当中可以访问结构当中的私有字段,类似与其他面向对象编程语言当中class中的方法可以访问其中的私有字段
封装
Go语言只有一种控制可见性的手段:大写首字母的标识符会从定义它们的包中被导出,小写字母的则不会。
公私有
方法、结构、变量、常量等,针对包的范围。
包