Go语言学习笔记—golang方法

本文是Go语言学习笔记,详细介绍了Go中的方法,包括方法的语法、实例演示、注意事项,以及方法接收者类型的区别。文章讨论了值类型结构体和指针类型结构体作为接收者时的不同行为,并对比了方法与函数的调用差异。最后,给出了两个练习题,以加深对方法应用的理解。

视频来源:B站《golang入门到项目实战 [2022最新Go语言教程,没有废话,纯干货!]》

文章为自己整理的学习笔记,侵权即删,谢谢支持!


一 golang方法简介

go语言没有面向对象的特性,也没有类对象的概念。但是,可以使用结构体来模拟这些特性,我们都知道面向对象里面有类方法等概念。我们也可以声明一些方法,属于某个结构体。

Go中的方法,是一种特殊的函数,定义于结构体struct之上(与struct关联、绑定),被称为struct的接受者(receiver)。通俗的讲,方法就是有接收者的函数。

1.1 语法

type mytype struct{}
 
func (recv mytype) my_method(para) retrun_type {}
func (recv *mytype) my_method(para) return_type {}

mytype:定义的一个结构体

recv:接受该方法的结构体(receiver)

my_method:方法名称

para:参数列表

return_type:返回值类型

从语法格式可以看出,一个方法和一个函数非常相似,只不过多了一个接受类型。

1.2 实例演示

package main
 
import "fmt"
 
type Person struct {
    name string
}
 
func (per Person) eat() {
    fmt.Println(per.name + " eating....")
}
 
func (per Person) sleep() {
    fmt.Println(per.name + " sleep....")
}
 
func main() {
    var per Person
    per.name = "tom"
    per.eat()
    per.sleep()
}

运行结果:

tom eating....
tom sleep....

1.3 注意事项

  1. 方法的receiver type并非一定要是struct类型,type定义的类型别名slicemapchannelfunc类型等都可以。
  2. 结构体struct结合它的方法就等价于面向对象中的类。只不过struct可以和它的方法分开,并非一定要属于同一个文件,但必须属于同一个包
  3. 方法有两种接收类型:(T Type)(T *Type),它们之间有区别。
  4. 方法就是函数,所以Go中没有方法重载(overload)的说法,也就是说同一个类型中的所有方法名必须都唯一
  5. 如果receiver是一个指针类型,则会自动解除引用。
  6. 方法和类型是分开的,意味着实例的行为和数据存储是分开的,但是它们通过receiver建立起关联关系。

二 golang方法接收者类型

结构体实例有值类型和指针类型,而方法的接收者是结构体,那么也有值类型和指针类型。区别就是接收者是否复制结构体副本

值类型复制,指针类型不复制。

2.1 值类型结构体和指针类型结构体

先看一段代码:

package main
 
import "fmt"
 
type Person struct {
    name string
}
 
func main() {
    p1 := Person{name: "tom"}
    fmt.Printf("p1: %T\n", p1)
    p2 := &Person{name: "tom"}
    fmt.Printf("p2: %T\n", p2)
}

运行结果:

p1: main.Person
p2: *main.Person

从运行结果,我们可以看出p1是值类型,p2是指针类型。

下面看一个传参结构体的例子:

package main
 
import "fmt"
 
type Person struct {
    name string
}
 
func showPerson(per Person) {
    fmt.Printf("per: %p\n", &per)
    per.name = "kite"
    fmt.Printf("per: %v\n", per)
}
 
func showPerson2(per *Person) {
    fmt.Printf("per: %p\n", per)
    // (*per).name
    per.name = "kite"
    fmt.Printf("per: %v\n", per)
}
 
func main() {
    p1 := Person{name: "tom"}
    fmt.Printf("p1: %p\n", &p1)
    showPerson(p1)
    fmt.Printf("p1: %v\n", p1)
    fmt.Println("---------------------")
    p2 := &Person{name: "tom"}
    fmt.Printf("p2: %p\n", p2)
    showPerson2(p2)
    fmt.Printf("p2: %v\n", p2)
}

运行结果:

p1: 0xc000050230
per: 0xc000050240
per: {kite}
p1: {tom}
---------------------
p2: 0xc000050270
per: 0xc000050270
per: &{kite}
p2: &{kite}

从运行结果,我们看到p1是值传递,拷贝了副本,地址发生了改变;而p2是指针类型,地址没有改变。

2.2 方法的值类型和指针类型接收者

值类型和指针类型接收者本质上和函数传参道理相同。

package main
 
import "fmt"
 
type Person struct {
    name string
}
 
func (per Person) showPerson() {
    fmt.Printf("per: %p\n", &per)
    per.name = "kite"
    fmt.Printf("per: %v\n", per)
}
 
func (per *Person) showPerson2() {
    fmt.Printf("per: %p\n", per)
    per.name = "kite"
    fmt.Printf("per: %v\n", per)
}
 
func main() {
    p1 := Person{name: "tom"}
    fmt.Printf("p1: %p\n", &p1)
    p1.showPerson()
    fmt.Printf("p1: %v\n", p1)
    fmt.Println("---------------------")
    p2 := &Person{name: "tom"}
    fmt.Printf("p2: %p\n", p2)
    p2.showPerson2()
    fmt.Printf("p2: %v\n", p2)
}

运行结果:

p1: 0xc000050230
per: 0xc000050240
per: {kite}
p1: {tom}
---------------------
p2: 0xc000050270
per: 0xc000050270
per: &{kite}
p2: &{kite}

三 方法和函数区别

  1. 调用方式不一样

    函数的调用方式: 函数名(实参列表)
    方法的调用方式: 变量.方法名(实参列表)

  2. 对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然

  3. 对于方法(如 struct 的方法),接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以

练习

① 编写结构体(MethodUtils),编程一个方法,方法不需要参数,在方法中打印一个 10*8 的矩形,在 main 方法中调用该方法。

package main

import (
	"fmt"
)

type Methodutiles struct {
}

func (mu Methodutiles) Print() {
	for i := 1; i <= 10; i++ {
		for j := 1; j <= 8; j++ {
			fmt.Print("*")
		}
		fmt.Println()
	}
}

func main() {
	var mu Methodutiles
	mu.Print()
}

运行结果:

********
********
********
********
********
********
********
********
********
********

② 定义小小计算器结构体(Calcuator),实现加减乘除四个功能
实现形式 1:分四个方法完成;
实现形式 2:用一个方法搞定。

实现形式1:

type Calcuator struct {
	Num1 float64
	Num2 float64
}

// 实现形式1:

func (cal *Calcuator) getSum() float64 {
	return cal.Num1 + cal.Num2
}

func (cal *Calcuator) getSub() float64 {
	return cal.Num1 - cal.Num2
}

func (cal *Calcuator) getPro() float64 {
	return cal.Num1 * cal.Num2
}

func (cal *Calcuator) getQuo() float64 {
	return cal.Num1 / cal.Num2
}

实现形式2:

type Calcuator struct {
	Num1 float64
	Num2 float64
}

func (cal *Calcuator) getRes(operator byte) float64 {
	res := 0.0
	switch operator {
	case '+':
		res = cal.Num1 + cal.Num2
	case '-':
		res = cal.Num1 - cal.Num2
	case '*':
		res = cal.Num1 * cal.Num2
	case '/':
		res = cal.Num1 / cal.Num2
	default:
		fmt.Println("运算符输入错误")
	}
	return res
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值