6.4. 第2节:方法

未匹配的标注
本文档最新版为 2023,旧版本可能放弃维护,推荐阅读最新版!

2 方法

2.1基本方法创建

在介绍面向对象时,讲过可以通过属性和方法(函数)来描述对象。
什么是方法呢?
方法,大家可以理解成就是函数,但是在定义使用方面与前面讲解的函数还是有区别的。
我们先定义一个传统的函数:
title
这个函数非常简单,下面定义一个方法,看一下在语法与传统的函数有什么区别:
方法的定义:
title

type Integer int :表示的意思是给int类型指定了一个别名叫Integer,别名可以随便起,只要符合GO语言的命名规则就可以。
指定别名后,后面可以用Integer来代替int 来使用。

func (a Integer) Test(b Integer) Integer{
}

表示定义了一个方法,方法的定义与函数的区别
第一:在关键字后面加上( a Integer), 这个在方法中称之为接收者,所谓的接受者就是接收传递过来的第一个参数,然后复制a, a的类型是Integer ,由于Integer是int的别名,所以a的类型为int
第二:在表示参数的类型时,都使用了对应的别名。
通过方法的定义,可以看出方法其实就是给某个类型绑定的函数。在该案例中,是为整型绑定的函数,只不过在给整型绑定函数(方法)时,一定要通过type来指定一个别名,因为int类型是系统已经规定好了,无法直接绑定函数,所以只能通过别名的方式。

第三:调用方式不同
var result Interger=3
表示定义一个整型变量result,并赋值为3.
result.Test( 3)
通过result变量,完成方法的调用。因为,Test( )方法,是为int类型绑定的函数,而result变量为int类型。所以可以调用Test( )方法。result变量的值会传递给Test( )方法的接受者,也就是参数a, 而实参Test( 3),会传递形参b.
当然,我们也可以将Test( )方法,理解成是为int类型扩展了,追加了的方法。因为系统在int类型时,是没有改方法的。

通过以上的定义,发现方法其实方法就是函数的语法糖

在以上案例中,Test( )方法是为int类型绑定的函数,所以任何一个整型变量,都可以调用该方法。
例如:

title

2.2给结构体添加方法

上面给整型创建了一个方法,那么直接通过整型变量加上“点”,就可以调用该方法了。
大家想一下,如果给结构体(类)加上了方法,那么根据结构体(类)创建完成对象后,是不是就可以通过对象加上“点”,就可以完成方法的调用,这与调用类中定义的属性的方式是完全一样的。这样就完成了通过方法与属性来描述一个对象的操作。(大家自己回想一下,前面在讲解对象的概念时,一直在说其具有的属性与行为(方法))

给结构体添加方法,语法如下:

title

给结构体添加方法的方式与前面给int类型添加方法的方式,基本一致。唯一不同的是,不需要给结构体指定别名,因为结构体Student就是相当于其所有成员属性的别名(id,name,score),所以这里不要在给结构体Student创建别名。
调用方式:根据结构体(类)创建的对象,完成了方法的调用。
PrintShow( )方法的作用,只是将结构体的成员(属性)值打印出来,如果要修改其对应的值,应该怎么做呢?
这时,大家肯定相当了指针,将方法的接收者,修改成对应的指针类型。
具体修改如下:
title

在创建方法时,接收者类型为指针类型,所以在调用方法时,创建一个结构体变量,同时将结构体变量的地址,传递给方法的接收者,然后调用EditInfo( )方法,完成要修改的数据传递。

在使用方法是,要注意如下几个问题:
第一:只要接收者类型不一样,这个方法就算同名,也是不同方法,不会出现重复定义函数的错误

但是,如果接收者类型一样,但是方法的参数不一样,是会出现错误的。

也就是,在GO中没有方法重载(所谓重载,指的是方法名称一致,参数类型,个数不一致)。

第二:关于接收者不能为指针类型。

2.3指针变量的方法值

接收者为普通变量,非指针,值传递

title
接收者为指针变量,引用传递
title

在上面的案例中,我们定义了两个方法,一个是PrintShow( ), 该方法的接收者为普通方法,一个EditInfo( )方法,该方法的接收者为指针变量,那么大家思考这么一个问题:定义一个结构体指针变量,能否调用PrintShow( )方法呢?如下所示:

title

通过测试,发现是可以调用的。
为什么结构体指针变量,可以调用PrintShow( )方法呢?
原因是:先将指针stu, 转换成*stu在调用。

等价如下代码:
title

所以,如果结构体变量是一个指针变量,它能够调用哪些方法,这些方法就是一个集合,简称方法集

如果是普通的结构体变量能否调用EditInfo( )方法。
title

是可以调用的,原因是:将普通的结构体类型的变量转换成(&stu)在调用EditInfo( )方法。

这样的好处是非常灵活,创建完对应的对象后,可以随意调用方法,不需要考虑太多指针的问题。

代码例子:

package main

import "fmt"

//1、定义函数类型  定义结构体名称
//2、为已存在的数据类型起别名
type Int int

//对象的方法

//func (对象)方法(参数列表)(返回值列表){
//  代码体
//}

func (a Int) add(b Int) Int {

    fmt.Println(a)
    fmt.Println(b)
    return a + b
}
func main() {

    //将源文件编译成可执行程序
    //编译过程
    //1、预处理 包展开 替换数据类型  去掉注释
    //2、编译  如果代码有错会提示 如果没错会编译成汇编文件
    //3、汇编  将汇编文件转成二进制文件
    //4、链接  将支持的库链接到程序中 变成可执行程序

    //类型别名会在编译时进行转换
    var a Int = 10

    var b Int =20
    //var b Int = 20
    //包.函数 结构体.成员 对象.方法
    //sum:=a.add(b)

    //对象.方法
    //想要使用方法 必须是相同类型的对象才可以
    sum:=b.add(a)
    fmt.Println(sum)
    //var b int = 20

    //fmt.Println(a + 10)

    //fmt.Printf("%T\n", a)

}

使用列子:

package main

import "fmt"

type student5 struct {
    name  string
    age   int
    sex   string
    score int
    addr  string
}

//函数
func Print() {
    fmt.Println("hello world")
}

//结构体类型可以作为对象类型
func (s student5) Print() {
    fmt.Printf("%p\n",&s)

    fmt.Printf("姓名:%s\n", s.name)
    fmt.Printf("年龄:%d\n", s.age)
    fmt.Printf("性别:%s\n", s.sex)
    fmt.Printf("成绩:%d\n", s.score)
    fmt.Printf("地址:%s\n", s.addr)
}

func main() {

    //创建对象
    stu := student5{"贾政", 58, "男", 80, "贾府"}
    fmt.Printf("%p\n",&stu)
    //对象.方法
    stu .Print()
    fmt.Println(stu)

    stu1 := student5{"贾玲", 29, "女", 99, "北京"}
    stu1.Print()

    //打印错误日志使用函数
    //print()
    //函数调用
    //Print()

    //对象的方法名可以和函数名重名  但是相同的对象方法名不能重名
    //fmt.Println(stu.Print)
    //fmt.Println(stu1.Print)
    //打印函数在代码区的地址
    fmt.Println(Print)
}

方法的内存模型:


package main

import "fmt"

type student6 struct {
    name  string
    age   int
    sex   string
    score int
    addr  string
}

//对象不同方法名相同 不会冲突
//在方法调用中 方法的接收者为指针类型
//指针类型 和普通类型表示是相同对象的类型

func (s *student6) Print() {
    s.name = "黄月英"
    s.score = 99
    //fmt.Println(*s)

}
func main() {
    stu := student6{"貂蝉", 22, "女", 80, "徐州"}

    //地址传递(*student6) 值传递(student6)
    //一般建议选择地址传递
    stu.Print()

    fmt.Println(stu)
}

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
wangchunbo
讨论数量: 0
发起讨论 只看当前版本


暂无话题~