go 方法

go 方法

什么是方法

方法其实就是一个函数,在 func 这个关键字和方法名中间加入了一个特殊的接收器类型。接收器可以是结构体类型或者是非结构体类型。接收器是可以在方法的内部访问的。

在其他语言中,方法是绑定给对象的,在go语言中,方法是绑定给结构体的。

下面就是创建一个方法的语法。

func (t Type) methodName(parameter list) {
}

示例:

func main() {
	cat := Cat{}
	cat.ChangeName("小猫")  // 方法绑定给了Cat结构体,可以通过结构体对象直接调用方法
	fmt.Println(cat)  // { 0}  下文解释
}

type Cat struct {
	name string
	age int
}
// 定义一个方法
func (cat Cat)ChangeName(name string)  {   // 注意: 方法不能与字段名重复
	cat.name = name			// cat 就是 python 中的 self
}
// 写成下面这样就和python中一样了
func (self Cat)ChangeName(name string)  {   
	self.name = name		
	fmt.Println(cat)   // {小猫 0}
}

方法和函数的区别

方法可以看作是特殊的函数,绑定在了结构体上,方法能实现的,使用函数也能实现,但是方法能够把结构体对象自动传入,而函数需要什么参数全都得传,所以方法相比于函数,使用起来更方便

var cat Cat=Cat{Name: "lxx",age: 38}
// 调用函数(正常调用,有几个值,就要传几个值)
PrintName(person)
// 调用方法(对象.方法,对象自动传入)
person.printName()

有了结构体,有了方法等同于面向对象中 类与对象 能够使用 对象.属性 对象.方法

总结:

  • Go 不是纯粹的面向对象编程语言,而且Go不支持类。因此,基于类型的方法是一种实现和类相似行为的途径。
  • 相同的名字的方法可以定义在不同的类型上,而相同名字的函数是不被允许的。

指针接收器与值接收器

到目前为止,我们只看到了使用值接收器的方法。其实还可以创建使用指针接收器的方法。值接收器和指针接收器之间的区别在于,在指针接收器的方法内部的改变能够影响外部对象,然而值接收器不能影响外部对象。

fmt.Println(cat)  // { 0}  下文解释

原因: 接收器跟函数传参一样,都是copy传参,传到方法中修改,不会影响原来的。

func main() {
	cat := Cat{}
	cat.ChangeName("小猫")
	fmt.Println(cat)  // { 0}
	cat.ChangeName2("白猫")  
	fmt.Println(cat)  // {白猫 0}
}

type Cat struct {
	name string
	age int
}
// 值接收器
func (cat Cat)ChangeName(name string)  {   // 注意: 方法不能与字段名重复
	cat.name = name
	fmt.Println(cat)
}
// 指针接收器
func (cat *Cat)ChangeName2(name string)  {   // 注意: 方法不能与字段名重复
	cat.name = name
	fmt.Println(cat)  // &{白猫 0}
}

使用指针类型接收器时机

  1. 当拷贝一个结构体的代价过于昂贵时,可以使用指针类型接收器
  2. 想修改原来的值,就要使用指针类型接收器

匿名字段的方法

属于结构体的匿名字段的方法可以被直接调用,就好像这些方法是属于定义了匿名字段的结构体一样。因此也可以将其叫做方法提升

func main() {
   cat1 := Ccat{"公猫",Cat{"花猫", 12}}
   fmt.Println(cat1)  // {公猫 {花猫 12}}
   cat1.ChangeName2("小花猫")
   fmt.Println(cat1)  // {公猫 {小花猫 12}}
}

type Cat struct {
   name string
   age int
}

type Ccat struct {
   sex string
   Cat
}

func (cat *Cat)ChangeName2(name string)  {   // 注意: 方法不能与字段名重复
   cat.name = name
   fmt.Println(cat)  // &{花猫属 12}
}

方法中值接收器和函数中值参数

当一个函数有一个值参数,它只能接受一个值参数。

当一个方法有一个值接收器,它可以接受值接收器和指针接收器。

func main() {
	cat := Cat{"白猫", 4}
	cat1 := &Cat{"白猫", 5}
	cat1.ChangeName("黑猫") // 可以传指针类型
	cat.ChangeName("黑猫")  // 也可以传值类型
	ChangeName(cat, "黑猫") //  支持传值类型,但是不支持指针类型
	fmt.Println(cat, cat1)  // {白猫 4} &{白猫 5}

}

type Cat struct {
	name string
	age  int
}
// 值类型方法
func (cat Cat) ChangeName(name string) {
	cat.name = name
	fmt.Println(cat) // {黑猫 4}
}
// 接收值类型参数
func ChangeName(cat Cat, name string) {
	cat.name = name
	fmt.Println(cat)  // {黑猫 5}
}
无论是值接收器还是值参数函数, 两者都不会影响原来的数据

方法中指针接收器和函数中指针参数

和值参数相类似,函数使用指针参数只接受指针,而使用指针接收器的方法可以使用值接收器和指针接收器。

func main() {
	cat := Cat{"白猫", 4}
	cat1 := &Cat{"白猫", 5}
	cat1.ChangeName2("黑猫") // 可以传指针类型
	cat.ChangeName2("黑猫")  // 也可以传值类型
	ChangeName1(cat1, "黑猫") //  支持传指针类型,但是不支持值类型
	fmt.Println(cat, cat1)  // {黑猫 4} &{黑猫 5}

}

type Cat struct {
	name string
	age  int
}

// 指针类型方法
func (cat *Cat) ChangeName2(name string) {
	cat.name = name
	fmt.Println(cat)  // &{黑猫 4}
}

// 接收指针类型参数
func ChangeName1(cat *Cat, name string) {
	cat.name = name
	fmt.Println(cat)  // &{黑猫 5}
}

结论:

  1. 使用方法比函数方便太多了
  2. 无论结构体对象是指针还是值,能否影响原数据都取决于方法中接收器的参数类型是值类型还是指针类型,所以方法写成值类型接收器还是指针类型接收器都可以,但是一般把结构体对象做成指针,以减少内存消耗

在非结构体上使用方法

到目前位置,我们只在结构体类型上定义方法。也可以在非结构体类型上定义方法,但是有一个问题。为了在一个类型上定义一个方法,方法的接收器类型定义和方法的定义应该在同一个包中。到目前为止,我们定义的所有结构体和结构体上的方法都是在同一个 main 包中,因此它们是可以运行的。

package main
func (a int) add(b int) {  // 报错
}
func main() {
}

在上面程序的第 3 行,我们尝试把一个 add 方法添加到内置的类型 int。这是不允许的,因为 add 方法的定义和 int 类型的定义不在同一个包中。

可以让该程序工作的方法是为内置类型 int 创建一个类型别名,然后创建一个以该类型别名为接收器的方法。

func main() {
	i := Myint8(1)
	i.add()
	i.add()
	fmt.Println(i)  // 3

}

type  Myint8 int8   // 给Myint8类型绑定add方法
func (i *Myint8) add()  {
	*i++
}

结构体取代类

类是一系列属性和方法的集合,结构体是一系列属性的集合,结构体+方法=类。

由上文结构体导出可以知道,结构体属性字段, 首字母小写就不能导出到外部包(隐藏属性), 但是我们可以在包内对该结构体通过方法,来实现外部包对内部隐藏属性的修改,案例入下:

test包

package test

import (
   "fmt"
)

type Dog struct {
   Name string
   age  int
}

func (dog Dog) ShowAge() {
   fmt.Println(dog.age)
}

func (dog *Dog) UpdateAge(age int) {
   // 这样使用可以在这里添加校验逻辑
   dog.age = age
}

main 包

package main
import test5 "day03/test"

func main() {
	dog := &test5.Dog{Name: "白狗"}
	dog.UpdateAge(5)
	dog.ShowAge()  // 5
}
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

go&Python

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值