自学go中(一)
内容偏向于实践,不会过度的说明语法内容。更多的专注于自己对go的一些认识和使用中让我感觉疑惑的内容。
指针,方法与接口
在go中你会看到各种地方都在使用指针,不管是receiver,参数(方法输入参数或者返回参数),了解指针的使用方法是非常重要的,不然会遇到各种问题。
指针的作用就不多说了(因为我是使用Java的,以前很少去关注指针的内存。在Java中如果使用的是对象,那么这个对象调用方法或者把这个对象作为参数出入到一个函数中,都是使用的这个对象的引用,其实就是这个对象的地址。)但是在go语言中就要重点区分指针和值类型了。
方法
方法在go中非常特殊(如果和Java或者C++这类语言去比较的话),在Java或者C++中,方法是属于某个类或者对象的(static方法是属于类的,其他的方法是属于对象自己的,所有当一个对象在调用自己的方法的时候,方法内对本身对象的内容作了修改是可以直接反正到这个对象身上的。所以Java对象或者C++对象在调用方法的时候是隐藏了this指针)。但是go中没有对象,只有结构体这个概念。如果严格的按照C语言的传统,go是没有面向对象的功能的。为了实现面向对象的功能,go中需要施加目标,显示的传递(不一定非要是指针,也可以是值类型)。
从上边内容就引出了方法的receiver,也就是方法有一个接受者。其实这个接受者类似于Java的调用这个方法的内容(java中的内容是对象,但是go中可以是引用——指针,也可以是值),如下所示:分别实现了两个方法fun1与fun2
type test struct {
a int32
b int32
}
func (t test) fun1() {
println(&t)
}
func (t *test) fun2() {
println(t)
}
但是方法的接受者不同,一个是值类型,一个是引用(指针)类型。在go中,值类型可以调用指针方法,指针也可以调用值类型方法,如下所示:
func main() {
var t test
t = test{1,2}
println(&t)
t.fun1()
t.fun2()
}
输出的结果为:
0x421111f70
0x421111f68
0x421111f70
可以看到结果,值类型调用接受者为值的方法的时候,传入方法内容的是这个值的拷贝,但是在调用指针类型的方法的时候,确实这个值对应的指针(毋庸置疑,fun2是可以对t中的值进行修改的,fun1是无法对值进行修改的)。
同样在go中,指针类型可以调用值方法,也可以调用指针方法,如下所示:
func main() {
var t *test
t = &test{1,2}
println(t)
t.fun1()
t.fun2()
}
输出结果为:
0x421111f70
0x421111f68
0x421111f70
从结果可以看出来,虽然值接受者被指针类型调用了,但是最终的结果可以看出来,效果和接受者有关。
目前的总结:不管真是调用方法的接受者是指针类型还是值类型,最终真是调用方法的类型与方法的声明相关。也就是指针类型的声明的方法,调用者如果是值类型会被转化为指针类型;如果是值类型声明的方法,调用这如果是指针类型会被转化为值类型。
接口与实现
接口的概念很简单,在go中接口的功能和其他语言中接口的功能是类似的。都是定义了一组功能的操作而已。
但是go中对接口的实现是不同,首先go中没有对象,类的概念,所有实现接口只能让struct结构体来做。所以一个结构体实现接口其实就是为这个结构体添加对应的接口的全部方法。但是我们知道,一个结构体的方法有两种类型(按照receiver是T或者是*T进行区分),当一个结构体在实现接口的时候,接口中的一个方法只能被这个结构体的值(T)类型实现或者被(*T)类型实现。
所以对于一个接口而言,要么是某个结构体的值实现了这个接口,要么是某个结构体的指针实现了这个接口
虽然一个接口中方法,某些可以让结构体的值实现,某些可以让结构体的指针实现,但是这种方式是不推荐的。
当在把接口作为函数的参数或者返回值的时候,在具体传入参数或者返回参数实现的时候,是需要考虑你返回的具体实现的结构体应该是指针类型的还是值类型的。(返回的类型依据实现接口的类型决定)。如果是按照指针类型实现的接口,那么都应该使用指针;如果是按照值类型实现的接口,那么可以传输值或者指针类型;如果既有值类型实现的方法,又有指针类型实现的方法,那么传输的时候应该传输指针类型的。
具体在调用接口的方法的时候的效果,与上小结方法的说明相同,也就是说如果实现这个方法的接受者是值,哪怕是指针调用,也只能实现值的效果
如下所示:一个接口say,实现的结构体hello
type say interface {
s1()
s2()
}
type hello struct {
a int32
b int32
c string
}
func (h *hello) s1() {
h.a = 100
}
func (h hello) s2() {
println(&h)
h.b = 200
}
func method(say say) {
println(say)
say.s2()
}
func main() {
hello := hello{}
println(hello.b)
method(&hello)
println(hello.b)
}
可以看到传入method的参数是hello的指针,并非结构体的值。如果把s1的方法的接受者改为值,那么method中传入的参数既可以是指针类型,也可以是值类型的。
重点提示:在没有特殊情况下,实现接口都直接使用指针的方法。
重点概念
- 语言关系:go和C语言之间是有语言关系的,但是和C++之间是一点关系都没有的。C语言中有结构体,go语言中也只有结构体。(但是C++中就是对象了)
问题
- 官方文档,go中可以值类型调用指针方法吗,或者指针方法调用值类型