方法
Golang中的方法是作用在指定的数据类型上的,即和指定的数据类型是绑定的,因此自定义类型(使用type定义的类型),都可以有方法(包括但不仅仅是struct类型)。
1.方法的声明和调用
> type A struct{
> Num int
> }
> func (a A)test(){
> a.Num = 10
> fmt.Println(a.Num)
> }
> func main(){
> var a A
> a.name = "john"
> a.test()
> }
对上述语法的说明
1)func (a A)test(){}
表示结构体A
,有一方法,方法名为test,其形参名为a(实质上是A类型的变量), 形参名可任意命名。
2)(a A)体现test方法是和A类型结构体绑定的。test()只能被A类型的变量调用,且不能直接调用。
案例说明1.
type Person struct {
Name string
}
func (person Person) test() {
person.Name = "tom"
fmt.Println("test()", person.Name) //tom
}
func main() {
var p Person
p.Name = "john"
p.test()
fmt.Println(p.Name) // john 值传递不影响实参
}
方法的使用
案例说明2
type Person struct {
Name string
}
func (person Person) test() {
person.Name = "tom"
fmt.Println("test()", person.Name) //tom
}
func (person Person) speak() {
fmt.Printf("%s是好人\n", person.Name)
}
func (person Person) calculator() {
sum := 0
for i := 1; i <= 1000; i++ {
sum += i
}
fmt.Println(sum)
}
func (person Person) calcul(n int) {
sum := 0
for i := 1; i <= n; i++ {
sum += i
}
fmt.Println(person.Name, "的计算结果是", sum)
}
func (person Person) getSum(n1, n2 int) int {
return n1 + n2
}
func main() {
var p Person
p.Name = "john"
p.test()
fmt.Println(p.Name) // john 值传递不影响实参
p.speak() // john是好人
p.calculator() //500500
p.calcul(10) //john 的计算结果是 55
res := p.getSum(10, 20)
fmt.Println(res) //30
}
2.方法的调用和传参机制
方法的调用和传参机制和函数基本一样,不同之处在于方法比函数多了其绑定类型变量的传送,方法绑定类型的变量会作为实参传递给方法。
案例说明:
type Cricle struct {
radius float64
}
func (cricle Cricle) area() float64 {
return math.Pi * math.Pow(cricle.radius, 2)
}
func main() {
c := Cricle{4.0}
res := c.area()
fmt.Printf("area=%.2f\n", res) //area=50.27
}
图例说明:
当方法绑定的变量是基础类型(值类型)时,调用方法的变量也会作为实参传递给方法,此时方法内调用的绑定类型的变量的内容来源于方法语句块内复制内容,而不是来源于绑定类型变量本身。
绑定类型的变量是引用类型时,则和上述情况不同。传递的变量是地址,指向相同的内容。
3.方法的声明(定义)
完整结构:
func (recevier type) methodName(参数列表)(返回值列表){
方法主体
return返回值
}
1)参数列表:表示方法输入信息
2)receiver type:表示这个方法和type类型绑定,或称之为该方法作用于type类型
3)receiver type:type可以是结构体,也可以是其它的自定义类。例如 type integer int, integer自定义类型也可以绑定方法。
4)receiver:就是type类型的一个变量(实例)
5)返回值列表:表示返回值,可以返回多个
6)方法主体:表示为了实现某一功能代码块
7)return 语句不是必须的
4.方法的注意事项和细节
1)结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递形式。
2)若希望装载方法中修改结构体变脸的值,可以通过结构体指针方式处理
案例演示
type Cricle struct {
radius float64
}
func (cricle Cricle) area() float64 {
return math.Pi * math.Pow(cricle.radius, 2)
}
// 往往为了提高传输效率,通常是将方法和指针类型绑定。
func (circle *Cricle) area1() float64 {
// circle.radius 等价于 (*circle).radius
circle.radius = 5.0
//标准写法:
// return math.Pi * math.Pow((*circle).radius,2)
// 由于Golang底层编译器设计时,允许circle.radius等价于(*circle).radius
// 故对于绑定的变量和其指针变量,所设计的方法不能重名,否则会报错。
return math.Pi * math.Pow(circle.radius, 2)
}
func main() {
c := Cricle{4.0}
res := c.area()
fmt.Printf("area=%.2f\n", res) //area=50.27
res1 := c.area1()
// 标准写法: res1 := (&c).area1()
// 在Golang中底层编译做了优化,认可 c.area1()等价于(&c).area1()
fmt.Printf("area=%.2f\n", res1) //area=78.54
}
Noting:
由于Golang底层编译器设计时,为操作简便,允许circle.radius等价于(*circle).radius,故对于方法绑定的变量或其指针变量,所设计的两者的方法名不能重名,否则会报错。
3)Golang中的方法作用在指定的数据类型上的,因此自定义类型都可以有方法,例如struct int float系等列都可以有方法。
案例演示
type integer int
// 值传递
func (i integer) test() {
fmt.Println("i=", i)
}
//引用传递
func (i *integer) test1() {
*i += 1
}
func main() {
var i integer = 10
i.test() // i=10
i.test1()
fmt.Println("i=", i) //i= 11
}
4)方法的访问范围控制规则和函数相同,即方法名首字母大写(类似publised)可以在本包和其它包使用;方法名首字母小写(类似privated)仅可以在本包内使用。
5)如果一个类型实现了String(),那么fmt.Println默认会调用这个变量的String()进行输出。
案例演示
type Student struct {
Name string
Age int
}
func (stu Student) String() string {
str := fmt.Sprintf("Name=[%v],Age=[%v]\n", stu.Name, stu.Age)
return str
}
func main() {
stu := Student{
Name: "ming",
Age: 18,
}
fmt.Println(stu) //Name=[ming],Age=[18]
fmt.Println(&stu) //Name=[ming],Age=[18]
// 会直接调用Student类型String()方法
}
5.方法和函数的区别
1)调用方法不一样:
函数的调用方式:函数名(实参列表)
方法的调用方式:变量.方法名(实参列表)
2)对于普通函数,接收者严格遵守其数据类型传递。即当接收者为值类型时,不能将指针类型的数据直接传递给函数,反之亦然。
案例演示:
type Person struct {
Name string
}
func test01(person Person) {
fmt.Println(person.Name)
}
func test02(person *Person) {
fmt.Println(person.Name) //person.Name 等价于 (*person).Name
}
func main() {
p := Person{Name: "Alex"}
test01(p) // Alex
// test01(&p) //报错
test02(&p) //Alex
// test02(p) //报错
3)对于方法而言,接收者为值类型时,支持直接使用指针类型的变量调用方法;同理,当接收者为引用指针类型时也可以直接使用值类型进行方法调用。但需要注意:**方法本质上是值传递还是地址传递取决于方法的 绑定变量类型,**即绑定变量是指针类型则传递的是地址,绑定类型是值类型则传递的是值。
案例演示:
func (p Person) test03() {
p.Name = "Tom"
fmt.Println(p.Name) //
}
func (p *Person) test04() {
p.Name = "Marry" //p.Name 等价于(*p.Name)
fmt.Println(p.Name)
}
func main() {
p := Person{Name: "Alex"}
// 值传递
p.test03() //Tom //标准写法
(&p).test03() //Tom
//从形式上是传入地址,但test03()绑定类型是值类型,所以本质上是值拷贝
fmt.Println(p.Name) //Alex
// 地址传递
(&p).test04() //Marry//标准写法
p.test04() //Marry
// p.test04()等价于(&p).test04()
//从形式上是值传递,但test03()绑定类型是指针类型,所以本质上是地址拷贝
fmt.Println(p.Name) //Marry
}