首页 > 数据库 > Go语言——方法
2020
01-03

Go语言——方法

前言

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++的同学同学应该很容易明白值引用和指针引用的区别,这里我们总结一下,有如下四种情况:

  1. 结构体值,如果调用的是值引用方法,在方法内部对变量的改变不会影响该结构;

  2. 结构体指针,如果调用的是指针引用方法,会在方法内部改变该结构内部变量的值; 

  3. 对于结构体值,如果调用的是指针引用方法,会改变结构体内的变量值;

  4. 对于结构体指针,如果调用的是值引用方法,不会改变结构内的变量值;

提供一个完整的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语言程序。

扫码芷若 获取免费视频学习资料

编程学习

查 看2019高级编程视频教程免费获取