• Go语言学习笔记—golang方法


    视频来源: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 {}
    
    • 1
    • 2
    • 3
    • 4

    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()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    运行结果:

    tom eating....
    tom sleep....
    
    • 1
    • 2

    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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    运行结果:

    p1: main.Person
    p2: *main.Person
    
    • 1
    • 2

    从运行结果,我们可以看出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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    运行结果:

    p1: 0xc000050230
    per: 0xc000050240
    per: {kite}
    p1: {tom}
    ---------------------
    p2: 0xc000050270
    per: 0xc000050270
    per: &{kite}
    p2: &{kite}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    从运行结果,我们看到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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    运行结果:

    p1: 0xc000050230
    per: 0xc000050240
    per: {kite}
    p1: {tom}
    ---------------------
    p2: 0xc000050270
    per: 0xc000050270
    per: &{kite}
    p2: &{kite}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    三 方法和函数区别

    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()
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    运行结果:

    ********
    ********
    ********
    ********
    ********
    ********
    ********
    ********
    ********
    ********
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    ② 定义小小计算器结构体(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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    实现形式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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    赞!图像几何三维重建代码实战教程来啦!深度计算+点云处理+网格重建优化+纹理贴图!
    实时数据平台设计
    BUUCTF 刮开有奖 1
    ORA-39002
    AVL树【图示详解+代码实现】
    如何分析软件测试中发现的Bug!
    基于Java的二手交易市场系统设计与实现
    素数计算 马蹄集
    Java-泛型
    如何实现云上 Lakehouse 高性能
  • 原文地址:https://blog.csdn.net/qq_39280718/article/details/125460227