0
点赞
收藏
分享

微信扫一扫

Go 方法定义与使用


方法

• 方法:作用在接收者上的函数(如果没有接受者的话,你要去调用一个函数,其实是去调用某一个包的函数,通过包名和函数名字来调用这个函数。如果是一个方法要先获得接受者的实例,然后通过实例来调用方法)


                func (recv receiver_type) methodName(parameter_list) (return_value_list)


• 使用场景


              很多场景下,函数需要的上下文可以保存在receiver属性中,通过定义 receiver 的方法,该方法可以直接访问 receiver 属性,减少参数传递需求



// StartTLS starts TLS on a server from NewUnstartedServer.


func (s *Server) StartTLS() {

if s.URL != “” {
panic(“Server already started”)
}

if s.client == nil {
s.client = &http.Client{Transport: &http.Transport{}
}
}

函数要入参数, 一个函数可能需要很多的入参,那么函数的参数列表就会非常的长了。但是将函数转化为一个具体接受者的方法都时候,所有参数就可以直接放在接受者里面就行了。

上面url client都不需要以入参的方式传递进来,在调用的时候直接通过接受者的属性拿出来就行了。

方法的好处是第一个可以缩短入参的复杂度,第二个就是可以为某一个对象赋予它具体的行为能力,使得我的程序更加面向对象。

 

  

 

不同于函数的方法

在 Go 语言中,方法和函数是两个概念,但又非常相似,不同点在于方法必须要有一个接收者,这个接收者是一个类型,这样方法就和这个类型绑定在一起,称为这个类型的方法。

在下面的示例中,type Age uint 表示定义一个新类型 Age,该类型等价于 uint,可以理解为类型 uint 的重命名。其中 type 是 Go 语言关键字,表示定义一个类型。

type Age uint

func (age Age) String(){

fmt.Println("the age is",age)

}

示例中方法 String() 就是类型 Age 的方法,类型 Age 是方法 String() 的接收者。

和函数不同,定义方法时会在关键字 func 和方法名 String 之间加一个接收者 (age Age) ,接收者使用小括号包围。

接收者的定义和普通变量、函数参数等一样,前面是变量名,后面是接收者类型。

现在方法 String() 就和类型 Age 绑定在一起了,String() 是类型 Age 的方法。

定义了接收者的方法后,就可以通过点操作符调用方法,如下面的代码所示:

func main() {

age:=Age(25)

age.String()

}

运行这段代码,可以看到如下输出:

the age is 25

接收者就是函数和方法的最大不同,此外,上面所讲到的函数具备的能力,方法也都具备。

提示:因为 25 也是 unit 类型,unit 类型等价于我定义的 Age 类型,所以 25 可以强制转换为 Age 类型。

值类型接收者和指针类型接收者

方法的接收者除了可以是值类型,也可以是指针类型。

定义的方法的接收者类型是指针,所以我们对指针的修改是有效的,如果不是指针,修改就没有效果,如下所示: 

func (age *Age) Modify(){

*age = Age(30)

}

调用一次 Modify 方法后,再调用 String 方法查看结果,会发现已经变成了 30,说明基于指针的修改有效,如下所示:

age:=Age(25)
age.String()
age.Modify()
age.String()

提示:在调用方法的时候,传递的接收者本质上都是副本,只不过一个是这个值副本,一是指向这个值指针的副本。

指针具有指向原有值的特性,所以修改了指针指向的值,也就修改了原有的值。

我们可以简单地理解为值接收者使用的是值的副本来调用方法,而指针接收者使用实际的值来调用方法。

示例中调用指针接收者方法的时候,使用的是一个值类型的变量,并不是一个指针类型,其实这里使用指针变量调用也是可以的,如下面的代码所示:

(&age).Modify()

这就是 Go 语言编译器帮我们自动做的事情:

  • 如果使用一个值类型变量调用指针类型接收者的方法Go 语言编译器会自动帮我们取指针调用,以满足指针接收者的要求。(&变量)
  • 同样的原理,如果使用一个指针类型变量调用值类型接收者的方法,Go 语言编译器会自动帮我们解引用调用,以满足值类型接收者的要求。(*变量)

总之,方法的调用者,既可以是值也可以是指针,不用太关注这些,Go 语言会帮我们自动转义,大大提高开发效率,同时避免因不小心造成的 Bug。

不管是使用值类型接收者,还是指针类型接收者,要先确定你的需求: 在对类型进行操作的时候是要改变当前接收者的值,还是要创建一个新值进行返回?这些就可以决定使用哪种接收者。

方法

有些函数是专门给特定的结构体对象使用的,比如对包外不可见的结构体调用可见方法来修改里面属性和获取里面属性,既然是对某个特定结构体的函数,那么有没有这种函数供特定结构体来调用呢? 也就是让函数的作用域是针对某种结构体。



所以这个时候就有了方法,方法是为特定类型定义的,只能由该类型调用的函数。



定义


方法是添加了接收者的函数,接收者必须是自定义的类型(方法就是在函数名称之前加了一个接收者,接受者就是调用者,只能由谁来调用)


type privateStruct struct {
privateAttr string
PublicAttr string
}

func NewPrivateStruct(attr1 string,attr2 string) *privateStruct {
return &privateStruct{attr1,attr2}
}


//因为返回的是指针类型,所以调用者/接收者 是指针类型结构体,这里返回就是接受者即谁来调用的
//方法 privateStruct结构体指针接受者方法
func (p *privateStruct) GetPrivateAttr() string{
return p.privateAttr
}

上面接收者也就是谁可以来调用这个函数,也就是哪种类型的来调用,上面是由privateStruct指针类型才可以去调用。

调用就是结构体对象方法的名称()。

 a := test.NewPrivateStruct("private","public")
fmt.Println(a.GetPrivateAttr())

函数调用的时候参数之间的传递相当于一个赋值的过程,一个结构体A赋值给结构体B,再去修改B对A是没有影响的。同理方法接受者不是结构体指针,而是结构体,那么要修改结构体里面的成员变量是不会成功的,对外面传递进来的结构体的成员是不会产生影响的(两个内存当中的数据,互不影响)。但是能够获取,因为传递值的时候复制过去了,返回的时候也是返回复制的值。

接受者只是指定了那种类型可以调用该函数,但是还是需要值传递的,下面是另外一种值调用(接受者是值类型,但是调用的时候是指针类型如何去调用)。

Go 方法定义与使用_golang

和上面是类似的,也不能修改成员变量。这样写是go的语法糖🍬,严格意义上是上面这种写法。

这种语法🍬只限于结构体变量在调用方法的时候。

Go 方法定义与使用_值类型_02

和调用的是指针类型还是值类型没有关系,只和接受者是有关系的。能不能修改决定于接受者,不是决定于调用者。

匿名嵌入


若结构体匿名嵌入带有方法的结构体时,则在外部结构体可以调用嵌入结构体的方法,并且在调用时只有嵌入的字段会传递给嵌入结构体方法的接收者。


当被嵌入结构体与嵌入结构体具有相同名称的方法时,则使用对象.方法名调用被嵌入结构体方法。若想要调用嵌入结构体方法,则使用对象.嵌入结构体名.方法。


Go 方法定义与使用_调用方法_03



Go 方法定义与使用_调用方法_04


Go 方法定义与使用_调用方法_05

举报

相关推荐

0 条评论