Go语言中的方法

1 Go语言中的方法

Go语言的方法声明和普通函数声明类似,方法是特殊的函数,只是在函数名前面多了一个参数,这个参数把这个方法绑定到这个参数对应的类型上的实例上,一个方法就是一个包含了接受者的函数。
Golang 方法总是绑定对象实例(就是实例来调用方法),并隐式将实例作为第一实参 (receiver)。

  1. 方法其实是接收者(或者说receive)上的一个函数。特殊类型的函数罢了
  2. 接受者可以是命名类型(不能直接是内置类型,可以给内置类型起个别名)或者结构体类型的一个值或者是一个指针

方法定义格式:

func (receiver type) methodName(参数列表)(返回值类型){}

参数和返回值可以省略。方法可以没有参数,也可以没有返回值。

接收者可以是一个值或者一个指针。

  • 接收者是命名类型:
package main

import "fmt"

type myint int //不能直接引用内置类型,但是可以给内置类型设置个别名

func (myi myint) tellTheAge() {
	fmt.Println("the age of mine is:", myi) //打印实例的年龄
}

func main() {
    //创建myint型的实例
	var leeAge myint = 23
    leeAge.tellTheAge()//实例来调用方法函数,相当于把leeAge传入到tellTheAge()中的(mui myint)里,打印出myi
    //控制台输出结果:the age of mine is 23
}

  • 接收者是结构体类型:
package main

import "fmt"

type person struct {
	name   string
	age    int
	gender rune
}

//创建person实例的工厂函数
func newPerson(n string, a int, g rune) person {
	return person{
		name:   n,
		age:    a,
		gender: g,
	}
}

//无参数列表和返回值类型的方法
func (p person) tellTheInfo() {
	fmt.Printf("我叫%s,性别%c,今年%d岁\n", p.name, p.gender, p.age)
}

//有参数列表但没有返回值类型的方法,此方法并不能改变person的信息,除非将接收者的类型改为指针型
func (p person) changeTheInfo(newname string, newage int, newgender rune) {
	p = person{
		name:   newname,
		age:    newage,
		gender: newgender,
	}
}

//有参数列表和返回值类型的方法,此函数并不能改变传进来的值,只是返回了一个新的person跟传进来的旧的person已经没有关系了
func (p person) changeAndGetTheInfo(newname string, newage int, newgender rune) person {
	p = person{
		name:   newname,
		age:    newage,
		gender: newgender,
	}
	return p
}

func main() {
	//1.调用无参数列表和返回值类型的方法
	//创建实例
	lee := newPerson("Lee", 23, '男')
	lee.tellTheInfo() //实例调用方法,相当于把lee实例的信息传入到tellTheInfo()中
	//控制台输出:我叫Lee,性别男,今年23岁

	//2.调用有参数列表但没有返回值类型的方法
	lee.changeTheInfo("Liam", 24, '女')
	lee.tellTheInfo()
	//控制台输出:我叫Lee,性别男,今年23岁
	//未能改变lee的信息

	//3.调用有参数列表和返回值类型的方法
	newp := lee.changeAndGetTheInfo("Liam", 24, '女')
	newp.tellTheInfo()
	//控制台输出:我叫Liam,性别女,今年24岁
	//调用changeAndGetTheInfo()其实已经算是创建了一个新的person了,这个新person即newp已经跟旧的person即lee已经没有关系了
	lee.tellTheInfo() //控制台输出:我叫Lee,性别男,今年23岁,lee还是没有改变其信息
}

2 接收者为值和指针类型的区别

接收者为值类型或指针类型,决定了调用方法时,方法对实例是值操作还是指针操作。

  • 若接收者为值类型,那么无论用值还是指针调用该方法,方法操作的都是对象的副本。
  • 若接收者为指针类型,那么无论用值还是指针调用该方法,方法操作的都是对象的指针。

以下我们通过实例来说明这一点。

定义一个结构体类型和该类型的一个方法,并使用值调用和指针调用的实例:

package main

import (
	"fmt"
)

type User struct {
	Name  string
	Email string
}

func (u User) Notify() { //接收者是User类型的一个值
	fmt.Printf("%v : %v \n", u.Name, u.Email)
}
func main() {
	// 值调用方法
	u1 := User{"golang", "golang@golang.com"}
	u1.Notify()
	// 指针调用方法
	u2 := User{"go", "go@go.com"}
	u3 := &u2
	u3.Notify()  //这两行可以直接写成(&u2).Notify()
}

输出结果:

golang : golang@golang.com 
go : go@go.com

解释: 首先我们定义了一个叫做 User 的结构体类型,然后定义了一个该类型的方法叫做 Notify,该方法的接受者是一个 User 类型的值。要调用 Notify 方法我们需要一个User类型的值或者指针

当方法的接受者是值类型而我们使用指针调用方法时,Go会自己调整使得调用可以被执行。但是要注意!当方法定义中的接受者不是一个指针,而是值类型时,该方法操作对应接受者的值的副本,意思就是即使你使用了实例的指针调用函数,但是方法的接受者是值类型,所以函数内部操作还是对副本的操作,而不是指针操作。

我们修改 Notify 方法,让它的接受者使用指针类型:

package main

import (
	"fmt"
)

type User struct {
	Name  string
	Email string
}

func (u *User) Notify() {
	fmt.Printf("%v : %v \n", u.Name, u.Email)
}
func main() {
	// 值调用方法
	u1 := User{"golang", "golang@golang.com"}
	u1.Notify()
	// 指针调用方法
	u2 := User{"go", "go@go.com"}
	u3 := &u2
	u3.Notify()
}

输出结果:

golang : golang@golang.com 
go : go@go.com

注意:当方法的接收者是指针时,就算用值来调用方法,方法内部也是对指针的操作,Go会自己调整。

方法不过是一种特殊的函数,只需将其还原,就知道接收者为 T 和 *T 的差别。

package main

import "fmt"

type Data struct {
   x int
}

func (self Data) valueTest() {
   fmt.Printf("Value:%p\n", &self)//接收者为值类型。相当于函数 func ValueTest(self Data)
}

func (self *Data) pointerTest() {
   fmt.Printf("pointer:%p\n", self)//接收者为指针类型。相当于函数 func pointerTest(self *Data)
}

func main() {
   a := Data{}
   pa := &a
   fmt.Printf("Data:%p\n", &a)

   //都是复制引用
   a.valueTest()//值类型调用接收者为值类型的方法
   pa.valueTest()//指针类型调用接收者为值类型的方法

   //都是地址引用
   a.pointerTest()//值类型调用接收者为指针类型的方法
   pa.pointerTest()//指针类型调用接收者为指针类型的方法
}

输出结果:

Data:0xc0000140a8
Value:0xc0000140c8
Value:0xc0000140e0
pointer:0xc0000140a8
pointer:0xc0000140a8

总结:

  1. 实例调用方法时很灵活,实例是值类型可以调用接收者类型为值类型或者指针类型的方法,实例是指针类型也同样可以调用接收者类型为值类型或者指针类型的方法
  2. 是值引用还是地址引用取决于定义方法时 接收者的类型
  3. 如果定义时,接收者是值类型,那么调用该方法时,操作的就是值的副本。
  4. 如果定义时,接收者是指针类型,那么调用该方法时,操作的就是指针。
  5. 与调用方法无关(与用实例的值还是指针来调用方法无关)。

3 普通函数与方法的区别

普通函数不如方法的调用方式如此灵活,普通函数定义参数是什么类型,那么调用时就只能用什么类型

  1. 对于方法,接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以(详细实例可看上一节)。
  2. 但对于普通函数,形参为值类型时,调用就只能用值类型的数据,而不能将指针类型的数据传递给它,反之亦然。
posted @   雪碧锅仔饭  阅读(113)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示