Go语言同时支持函数和方法,方法是包含接收器的函数,接收器可以是命名类型或结构体类型的值或指针。为特定类型定义的方法属于该类型的方法集。
- 方法在接收者参数的帮助下,可访问接收者的属性。
- 方法的接收者可以是结构体类型或非结构体类型。
方法会在func
关键字和方法名之间添加一个特殊的接收器类型,接收器可以是结构体或非结构体类型,接收器可以在方法内部被访问。
func (receiver_name T)method_name(parameter_list) (return_type){
}
类型T上所有方法的集合称之为类型T的方法集
Go语言不是纯粹的面向对象的编程语言,Go语言不支持类。因此,基于类型的方法是一种实现和类相似行为的途径。
Go语言中结构体像是类的一种简化形式,如果将结构体视为类,则类的方法就是作用在接收器上的函数。接收本身是某种类型的变量,因此方法是一种特殊类型的函数。
Go语言支持方法,方法与函数相似,不同之处在于方法中会包含一个接收者参数。创建方法时,接收者和接收者类型必须出现在同一个包中,禁止创建方法时的接收者在其它包中已经被定义。
接收器类型可以是任何类型,但接收器不能是接口类型,因为接口是一个抽象的定义,而方法却是具体的实现。另外,接收器不能是一个指针类型,但可以说任何其他运行类型的指针。
虽然类型与方法使结构体等价于面向对象中的累,但Go语言中类型的代码和绑定在其上的方法的代码可以不用放置在一起,因此可以存在于不同的源文件中,唯一的要求是必须在同一个包中。
方法VS函数
方法和函数定义区别在于方法的实例可以接受参数,编译器以此确定方法所属的类型。其它语言中,尽管没有显式地定义但也会在调用时隐式地传递this
实例参数。
Go语言中函数是不属于任何结构体、类型的方法,函数是没有接收器的,而方法则是具有接收器的,因此方法要么属于一个结构体,要么属于一个新定义的类型。
Go语言中相同名字的方法可以定义在不同的类型上,但相同名字的函数是不被允许的。
由于方法本质上是函数,因此不允许方法重载,对于一个类型只能有一个给定名称的方法。若基于接收器的类型,则可以重载。具有相同名字的方法可以在不同的接收器类型上存在,比如同一个包中。
面向对象语言中类拥有的方法表示类所具有的行为,在Go语言中方法也是如此,只是Go语言建立的接收器会强调方法的作用对象是接收器即类的实例,而函数是没有作用对象的。
方法 | 函数 |
---|---|
包含接收器 | 不包含接收器 |
可接受指针和值 | 禁止同时接受指针和值 |
可定义同名非同类型的方法 | 禁止定义同名非同类型的函数 |
接收器
方法的接收器支持值接收器和指针接收器,二者的区别在于指针接收器方法内部的改变对于调用者是可见的,值接收器则不可见。
func (接收器变量 接收器类型) 方法名(参数列表) (返回参数) {
方法体
}
- 接收器变量
接收器中的参数变量命名时,官方建议使用接收器类型名称的首字母的小写,而非self、this之类的命名。 - 接收器类型
接收器类型和参数类似,可以是指针类型或非指针类型 - 方法名、参数列表、返回值参数与函数定义相同
Go语言中允许定义接收者为结构体类型的方法,方法内部可访问接收器字段。
package main
type User struct {
Name string
Salary int
}
func (receiver User) test() {
println(receiver.Name)
}
func main() {
user := User{Name:"admin", Salary: 1000}
user.test()
}
Go语言中只要类型和方法定义在同一个包中,即可使用非结构体类型接收器创建方法。若存在int
、string
等不同的包中,则编译器会抛出错误。
package main
type integer int
func (receiver integer) add(value integer) integer{
return receiver + value
}
func main() {
v1 := integer(1)
v2 := integer(2)
result := v1.add(v2)
println(result)//3
}
Go语言中允许使用指针接收器创建方法,在指针接收器的作用下,对方法中所做的更改将反映到调用方中。
func (ptr *Type) method_name(...Type) Type {
}
package main
type User struct {
Id int
Name string
}
//使用User类型的接收者
func (this *User) SetName(name string){
(*this).Name = name
}
func main() {
//初始化结构体
user := User{Id:1, Name:"admin"}
//创建指针
ptr := &user
//使用指针调用方法
ptr.SetName("root")
println(user.Name)//root
println(ptr.Name)//root
}
方法可以接受指针和值
Go语言中当一个函数具有值参数时仅会接受参数的值,若将指针传递给值函数则不会被接受,反之亦然。但Go语言的方法可以接受值和指针,无论是使用指针还是值接收器定义的。
package main
type User struct {
Id int
Name string
}
//带指针的方法
func (this *User) SetName(name string){
(*this).Name = name
}
//带值得方法
func (this User) GetName(){
println(this.Name)
}
func main() {
//初始化值
user := User{Id:1, Name:"admin"}
user.SetName("root")
user.GetName()//root
(&user).GetName()//root
}
值接收器
当方法作用于非指针接收器时,Go语言会在代码运行时将接收器的值复制一份,在非指针接收器的方法中可以获取接收器的成员值,但修改后无效。
当使用值接收器声明的方法,调用时会使用值的副本来执行,因此该类型的值并不会被改变。
package main
import "fmt"
type User struct {
Id int
Name string
}
func (this User) SetName(name string){
this.Name = name
}
func (this User) print(){
fmt.Printf("id = %v, name = %v\n", this.Id, this.Name)
}
func main() {
user := User{Id:1, Name:"admin"}
user.SetName("root")
user.print()//id = 1, name = admin
}
使用指针来调用使用值接收器声明的方法时,指针被解引为值的副本,因此原指针变量指向的值不会发生改变。
package main
import "fmt"
type User struct {
Id int
Name string
}
func (this User) SetName(name string){
this.Name = name
}
func (this User) print(){
fmt.Printf("id = %v, name = %v\n", this.Id, this.Name)
}
func main() {
user := &User{Id:1, Name:"admin"}
user.SetName("root")
user.print()//id = 1, name = admin
}
指针接收器
指针类型的接收器由结构体的指针组成,更接近于面向对象中的this或self。
由于指针的特性,调用方法时,修改接收器指针的成员变量,在方法结束后修改都是有效i的。
使用类型的指针调用指针接收器声明的方法时,该方法会共享指针指向的值,因此会改变指针指向的☞。
package main
import "fmt"
type User struct {
Id int
Name string
}
func (this *User) SetName(name string){
this.Name = name
}
func (this User) print(){
fmt.Printf("id = %v, name = %v\n", this.Id, this.Name)
}
func main() {
user := &User{Id:1, Name:"admin"}
user.SetName("root")
user.print()//id = 1, name = root
}
适用类型的值调用指针接收器声明的方法时,该方法会共享所指向的值,此时会改变指针指向的值。
package main
import "fmt"
type User struct {
Id int
Name string
}
func (this *User) SetName(name string){
this.Name = name
}
func (this User) print(){
fmt.Printf("id = %v, name = %v\n", this.Id, this.Name)
}
func main() {
user := User{Id:1, Name:"admin"}
user.SetName("root")
user.print()//id = 1, name = root
}
网友评论