前言
Go语言作为一种类C的高级语言,在降低了C指针编程的门槛的同时,也增加了一些潜在的编程陷阱,甚至对于熟练使用Go语言的老手,也会不小心踩坑,本文对Go语言可能混淆的一些概念进行讨论,并辅以实例进行说明,同时也会总结Go语言编程的一些技巧。
方法定义
在声明函数时,在函数名前放上一个变量(可以称变量为接收器receiver),即声明了一个方法。我们通常定义一个结构体struct{},然后基于这个结构体定义一系列方法,实现面向对象中的类的概念。
接收器即可以是普通类型,也可以是指针类型。
type T int func (t T) f() { /* ... */ } func (t *T) f2() { /* ... */ }
此外,需要注意的是为避免歧义,在声明方法时,如果一个类型本身是一个指针的话,是不允许出现在接收器中的,比如下面这个例子:
type T *int func (T) f() { /* ... */ } // compile error: invalid receiver type T (T is a pointer type)
我们也不能通过一个无法取地址的接收器来调用指针方法,比如临时变量的内存地址就无法获取:
t := T(1) t.f2() T(1).f2() //compile error: cannot take the address of T(1)
方法也可以用nil指针作为其接收器:
t := T(1) t2 := &t t2 = nil // nil receiver t2.f2()
方法调用陷阱
在调用方法时,Go编译器会根据我们定义方法的接收器,对变量做隐式转换。因此我们必须要注意,这种转换可能让我们的程序达不到我们预期想要的效果。熟悉C/C++的同学同学应该很容易明白值引用和指针引用的区别,这里我们总结一下,有如下四种情况:
-
结构体值,如果调用的是值引用方法,在方法内部对变量的改变不会影响该结构;
-
结构体指针,如果调用的是指针引用方法,会在方法内部改变该结构内部变量的值;
-
对于结构体值,如果调用的是指针引用方法,会改变结构体内的变量值;
-
对于结构体指针,如果调用的是值引用方法,不会改变结构内的变量值;
提供一个完整的example.go例子如下:
package main import ( "fmt" ) //MethodExample 验证结构体 type MethodExample struct { value int } //Increase 自增:值引用 func (s MethodExample) Increase() { s.value++ } //Decrease 自减:指针引用 func (s *MethodExample) Decrease() { s.value-- } func main() { //1.值接收者,不改变value的值 methodExample1 := MethodExample{1} methodExample1.Increase() fmt.Println(methodExample1.value) //2.指针接收者,改变value的值 methodExample2 := &MethodExample{1} methodExample2.Decrease() fmt.Println(methodExample2.value) //3.值接收者,改变value的值 methodExample3 := MethodExample{1} methodExample3.Decrease() fmt.Println(methodExample3.value) //4.指针接收者,不改变value的值 methodExample4 := &MethodExample{1} methodExample4.Increase() fmt.Println(methodExample4.value) }
运行结果如下:
1 0 0 1
可以看到:
- methodExample1是普通的对象,调用普通变量接收器的Increase自增方法,并没有改变对象自身的value值;
- methodExample2是指针对象,调用指针变量接收器的Decrease自减方法,对象自身的value值被减一;
- methodExample3是普通的对象,调用指针变量接收器的Decrease自减方法,对象自身的value值被减一;
- methodExample4是指针对象,调用普通变量接收器的Increase自增方法,并没有改变对象自身的value值;
扩展对象
类似于C++中的继承概念,我们在Go中,也可以将一个对象嵌入到另一个对象中,对对象进行扩展。扩展的对象也就能调用子对象中的方法
type A struct {} func (a A) func1() error { return nil } type B struct { A } func main() { b := B{} b.func1() }
方法表达式
在调用接收器的方法时,我们通常的做法如下:
type A struct {} func (a A) func1() error { return nil } func main() { a := A{} a.func1() }
我们可以将此过程分为两步,首先得到方法的表达式也叫选择器:
f := a.func1
然后再调用此方法表达式,此时不用传入接收器,可直接调用:
f()
这两种对方法的调用方式是等价的。
至此,我们已经基本熟悉了方法使用中,可能会遇到的一些迷惑的地方,以及一些高级特性,基于此,我们可以实现出更为高效稳健的Go语言程序。
扫码二维码 获取免费视频学习资料
- 本文固定链接: http://phpxs.com/post/6886/
- 转载请注明:转载必须在正文中标注并保留原文链接
- 扫码: 扫上方二维码获取免费视频资料