Golang方法和函数区别

117 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第12天,点击查看活动详情

在前面的章节中介绍过了Golang中的方法和函数,但是如果不进行比较,对于两者的认识还是很容易产生混淆。

一、概述

Go语言支持方法。Go方法与Go函数相似,但有一点不同,方法就是一个包含了接收者参数的函数

  • 在接收者参数的帮助下,该方法可以访问接收者的属性。
  • 接收方可以是结构类型或非结构类型。
  • 在代码中创建方法时,接收者和接收者类型必须出现在同一个包中。而且不允许创建一个方法,其中的接收者类型已经在另一个包中定义,包括像int、string等内建类型。如果您尝试这样做,那么编译器将抛出错误。

语法

func(reciver_name Type) method_name(parameter_list)(return_type){
    // Code
}

在此,可以在方法内访问接收器。

示例

type person struct {
	name string
	age int8
}

//函数,求和
func sum(x,y int) int {
	return x + y
}

//方法
func (p person) String() string{
	return fmt.Sprintf("姓名:%s\n年龄:%d",p.name,p.age)
}
func main() {
	//初始化并调用方法
	p := person{name:"Leefs",age:18}
	fmt.Println(p.String())
	//调用函数
	x,y := 10,20
	s := sum(x,y)
	fmt.Printf("sum=%d\n",s)
}

运行结果

姓名:Leefs
年龄:18
sum=30

总结

方法在定义的时候,会在func和方法名之间增加一个参数,这个参数就是接收者,这样我们定义的这个方法就和接收者绑定在了一起,称之为这个接收者的方法。

二、同名方法

在 Go 中,相同的名字的方法可以定义在不同的类型上,而相同名字的函数是不被允许的。如果在GO语言程序中添加一个同名函数,就会报错。但是在不同的结构体上面定义同名的方法就是可行的。

type person struct {
	name   string
	age int8
}

//同名方法,返回姓名和年龄
func (p person) String() string {
	return fmt.Sprintf("姓名:%s,年龄:%d",p.name,p.age)
}

type student struct {
	name string
}

//同名方法,返回学生姓名
func (s student) String() string{
	return fmt.Sprintf("学生姓名:%s",s.name)
}

func main() {
	p := person{name: "Leefs",age:  18}
	fmt.Println(p.String())
	s := student{name: "Jeyoo"}
	fmt.Println(s.String())
}

运行结果

姓名:Leefs,年龄:18
学生姓名:Jeyoo

三、值传递和引用传递

  • 值传递:值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。

  • 引用传递:引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

示例

type person struct {
	name string
}

//值传递
func (p person) changeName(name string){
	p.name = name
}

//引用传递
func (p *person) changeNamePointer(name string){
	p.name = name
}

func main() {
	p := person{name:"Leefs"}
	fmt.Printf("原始名称:%s\n",p.name)
     //修改失败
	p.changeName("Took")
	fmt.Printf("通过值传递修改名称:%s\n",p.name)
    //修改成功
	p.changeNamePointer("Jeyoo")
	fmt.Printf("通过引用传递修改名称:%s\n",p.name)
}

运行结果

原始名称:Leefs
通过值传递修改名称:Leefs
通过引用传递修改名称:Jeyoo

在调用方法的时候,传递的接收者本质上都是副本,只不过一个是这个值副本,一是指向这个值指针的副本。指针具有指向原有值的特性,所以修改了指针指向的值,也就修改了原有的值。我们可以简单的理解为值接收者使用的是值的副本来调用方法,而指针接收者使用实际的值来调用方法。

四、在方法中使用值接收器与在函数中使用值参数

当一个函数有一个值参数,它只能接受一个值参数。当一个方法有一个值接收器,它可以接受值接收器和指针接收器。

type Lesson struct {
	Name string
}

//方法
func (lesson Lesson) PrintInfo() {
	fmt.Println(lesson.Name)
}

//函数
func PrintInfo(lesson Lesson) {
	fmt.Println(lesson.Name)
}

func main() {
	lesson := Lesson{"《张宇基础30讲》"}
	//调用函数
	PrintInfo(lesson)
	//调用方法
	lesson.PrintInfo()

	bPtr := &lesson
	//调用函数
	//PrintInfo(bPtr) // error
	//调用方法
	bPtr.PrintInfo()
}

在上面的程序中,使用值参数 PrintInfo(lesson) 来调用这个函数是合法的,使用值接收器来调用 lesson.PrintInfo() 也是合法的。

然后在程序中我们创建了一个指向 Lesson 的指针 bPtr ,通过使用指针接收器来调用 bPtr.PrintInfo() 是合法的,但使用值参数调用 PrintInfo(bPtr) 是非法的。

五、在非结构体上的方法

不仅可以在结构体类型上定义方法,也可以在非结构体类型上定义方法,但是有一个问题。为了在一个类型上定义一个方法,方法的接收器类型定义和方法的定义应该在同一个包中

示例

package main

import "fmt"

type myInt int

func (a myInt) add(b myInt) myInt {
    return a + b
}

func main() {
    var x myInt = 50
    var y myInt = 7
    fmt.Println(x.add(y))   // 57
}

总结

方法和函数之间的差异:

方法函数
包含接收器不包含接收器
可以接受指针和值不能同时接受指针和值
可以在程序中定义相同名称但不同类型的方法程序中不允许定义相同名称但不同类型的函数