Go语言——方法

简介: Go语言——方法

方法

□ 概述

本质上,方法是一个和特殊类型关联的函数。

⽅法总是绑定对象实例,并隐式将实例作为第⼀实参 (receiver),方法的语法如下:

// 方法
func (receiver ReceiverType) funcName (parameters) (results)
// 参数 receiver 可任意命名。如⽅法中未曾使⽤,可省略参数名。
// 参数 receiver 类型可以是 T 或 *T。基类型 T 不能是接⼝或指针。
// 不支持重载方法,也就是说,不能定义名字相同但是不同参数的方法。

方法与函数的区别:

函数是⼀段具有独⽴功能的代码,可以被反复多次调⽤,从⽽实现代码复⽤。⽽⽅法是⼀个类的⾏为功能,只有该类的对象才能调⽤。

Go语⾔的⽅法method是⼀种作⽤于特定类型变量的函数,这种特定类型变量叫做Receiver(接受者、接收者、接收器);

接受者的概念类似于传统⾯向对象语⾔中的this或self关键字;

⼀个⽅法就是⼀个包含了接受者的函数;

Go语⾔中, 接受者的类型可以是任何类型,不仅仅是结构体, 也可以是struct类型外的其他任何类型。

type Employee struct {
  name, currency string
  salary         int
}
//参数为Employee类型 的函数
func displaySalary(e Employee) {
  fmt.Printf("员工姓名: %s, 薪资: %s%d\n", e.name, e.currency, e.salary)
}
//接收者类型为Employee 的方法
func (e Employee) displaySalary() {
  fmt.Printf("员工姓名: %s, 薪资: %s%d\n", e.name, e.currency, e.salary)
}
func main() {
  emp1 := Employee{
    name:     "纱布",
    salary:   2000,
    currency: "$", // 货币单位
  }
  emp1.displaySalary()  //调用方法 员工姓名: 纱布, 薪资: $2000
  displaySalary(emp1)   //调用函数 员工姓名: 纱布, 薪资: $2000
}

□ 为类型添加方法

  1. 基础类型作为接收者
type MyInt int //自定义类型,给int改名为MyInt
// 方法
func (a MyInt) Add(b MyInt) MyInt { //面向对象
  return a + b
}
// 传统方式的定义
func Add(a, b MyInt) MyInt { //面向过程
  return a + b
}
func main() {
  // 基础类型作为接收者
  var a MyInt = 1
  var b MyInt = 1
  //调用func (a MyInt) Add(b MyInt)
  fmt.Println("a.Add(b) = ", a.Add(b)) //a.Add(b) =  2
  //调用func Add(a, b MyInt)
  fmt.Println("Add(a, b) = ", Add(a, b)) //Add(a, b) =  2
}

通过上面的例子可以看出,面向对象只是换了一种语法形式来表达。

方法是函数的语法糖,因为receiver其实就是方法所接收的第1个参数。

结构体作为接收者

type Person struct {
    name string
    sex  byte
    age  int
}
func (p Person) PrintInfo() { //给Person添加方法
    fmt.Println(p.name, p.sex, p.age)
}
func main() {
    p := Person{"手嘎吧", '男', 18} //初始化
    p.PrintInfo() //调用func (p Person) PrintInfo()
}

□ 值语义和引用语义

值语义 :值作为接收者,在方法中对对象的改变 出了方法就没用了。

引用语义:指针作为接收者,在方法中对对象的改变 出了方法仍然有效。

type Person34 struct {
  name string
}
// 值作为接收者,引用语义
func (p34 Person34) printInfo01() {
  p34.name = "张删"
}
// 指针作为接收者,引用语义
func (p34 *Person34) printInfo02() {
  p34.name = "历时"
}
func main() {
  p34:=Person34{"糊涂"}
  p34.printInfo01()
  fmt.Println(p34.name) // 糊涂
  p34.printInfo02()
  fmt.Println(p34.name) // 历时
}

□ 方法集

方法集是指可以被该类型的值可调用的所有方法的集合。

类型 *T 方法集(接受者为值或者指针 都可去调用)

一个 指向自定义类型的值 的指针,它的方法集由该类型定义的所有方法组成,无论这些方法接受的是一个值还是一个指针。

如果在指针上调用一个接受值的方法,Go语言会聪明地将该指针解引用,并将指针所指的底层值(那片内存)作为方法的接收者。

type Person34 struct {
  name string
}
// 指针作为接收者,引用语义
func (p34 Person34) printInfoPrinter() {
  p34.name = "张删"
}
// 值作为接收者,引用语义
func (p34 *Person34) printInfoValue() {
  p34.name = "历时"
}
func main() {
  p34:= &Person34{"糊涂"}
  p34.printInfoPrinter()
  fmt.Println(p34.name) // 糊涂
  (*p34).printInfoValue()
  fmt.Println(p34.name) // 历时
  p34.printInfoValue()
  fmt.Println(p34.name) // 历时
}

类型 T 方法集(不可直接调接受者类型为指针的方法)

一个 自定义类型值 的 方法集 则由为 该类型定义的接收者类型为值类型的方法组成,但是不包含那些接收者类型为指针的方法。

但这种限制通常并不像这里所说的那样,因为如果我们只有一个值,仍然可以调用一个接收者为指针类型的方法,这可以借助于Go语言传值的地址能力实现。

p34:= Person34{"糊涂"}
(&p34).printInfoPrinter()
fmt.Println(p34.name) // 历时

□ 方法的继承与重写

  1. 方法的继承、
    如果匿名字段实现了一个方法,那么包含这个匿名字段的struct也能调用该方法。
type Person35 struct {
  name string
}
func (p Person35) fatherMethod()  {
  fmt.Println("你继承了父类的方法")
}
type Student35 struct {
  Person35
  name string
}
func main() {
  stu:=Student35{Person35{"糊涂啊"},"糊涂孩纸"}
  // 继承父类的方法
  stu.fatherMethod()
}

方法的重写

// 父类方法
func (p Person35) fatherMethod() {
  fmt.Println(p.name)
}
// 重写 父类的方法
func (s Student35) rewriteFather()  {
  fmt.Println(s.name)
}
func main() {
  per := Person35{"父名"}
  stu := Student35{Person35{"子名"}, 12}
  // 重写父类的方法
  per.fatherMethod() // 父名
  stu.fatherMethod() // 子名
}

□ 表达式

type Person36 struct {
  id   int
  name string
  age  int
}
func (p Person36) PrintInfoValue() {
  fmt.Printf("%p,%v\n", &p, p)
}
// 建议使用这种指针类型的
func (p *Person36) PrintInfoPointer() {
  fmt.Printf("%p,%v\n", &p, *p)
}

方法值( 隐式传参 )

就是实例化之后的对象去调用 绑定该对象的方法名 这时候结果是一个func()类型的值,这个就是方法值,去赋值给一个变量。

func main() {
  p := Person36{1, "condition", 18}
// 值传递
  // 可以看出调用方法名的结果是个地址 说明也可以作为值去传递
  fmt.Println(p.PrintInfoPointer)        // 0x6f9a00
  fmt.Printf("%T\n", p.PrintInfoPointer) // func()
  //方法值,隐式传递 receiver,绑定实例(对象)
    //(意思就是这还是个属于Person35的方法,且绑定了实例化对象p,但是你并没有显式的赋予给pFunc1)
  pFunc1 := p.PrintInfoPointer
  pFunc1() // 0xc000006038,{1 condition 18}
  pFunc2 := p.PrintInfoValue
  pFunc2() // 0xc000042440,{1 condition 18}
}

方法表达式(显示传参)

func main() {
  p := Person36{1, "condition", 18}
// 方法表达式
  pFunction1 := Person36.PrintInfoValue
  fmt.Printf("%T\n",pFunction1) // func(main.Person36)
  // pFunction1() 错误写法:因为没有绑定实例化的对象,需要传入实例化后的对象
  pFunction1(p) // 0xc000042480,{1 condition 18}
  // 不同于方法值的地方 对于参数的类型是严格要求的 要求参数为指针时,就必须为指针。
  pFunction2 := (*Person36).PrintInfoPointer
  fmt.Printf("%T\n",pFunction2) // func(*main.Person36)
  pFunction2(&p) // 0xc000006038,{1 condition 18}
}
相关文章
|
7天前
|
Go
Go 语言循环语句
在不少实际问题中有许多具有规律性的重复操作,因此在程序中就需要重复执行某些语句。
17 1
|
7天前
|
Go 开发者
探索Go语言的并发之美
在Go语言的世界里,"并发"不仅仅是一个特性,它是一种哲学。本文将带你领略Go语言中goroutine和channel的魔力,揭示如何通过Go的并发机制来构建高效、可靠的系统。我们将通过一个简单的示例,展示如何利用Go的并发特性来解决实际问题,让你的程序像Go一样,轻盈而强大。
|
8天前
|
JSON Go API
使用Go语言和Gin框架构建RESTful API:GET与POST请求示例
使用Go语言和Gin框架构建RESTful API:GET与POST请求示例
|
8天前
|
Go
go语言创建字典
go语言创建字典
|
5天前
|
大数据 Shell Go
GO方法与自定义类型
本文详细介绍了 Go 语言中的自定义数据类型与方法。不同于传统的面向对象编程语言,Go 通过结构体 (`struct`) 和方法 (`method`) 来扩展自定义类型的功能。文章解释了如何定义结构体、创建方法,并探讨了值接收器与指针接收器的区别及应用场景。此外,还介绍了方法的可见性以及接收器的命名惯例。通过具体示例,帮助读者更好地理解和应用这些概念。
|
9天前
|
安全 Go 数据处理
探索Go语言的并发之美:Goroutines与Channels
在Go语言的世界里,"并发"不仅仅是一个概念,它是一种生活的方式。本文将带你领略Go语言中Goroutines和Channels的魔力,它们是如何让并发编程变得既简单又高效。我们将通过一个简单的示例,展示如何使用这些工具来构建一个高性能的网络服务。
|
9天前
|
关系型数据库 Go 数据处理
高效数据迁移:使用Go语言优化ETL流程
在本文中,我们将探索Go语言在处理大规模数据迁移任务中的独特优势,以及如何通过Go语言的并发特性来优化数据提取、转换和加载(ETL)流程。不同于其他摘要,本文不仅展示了Go语言在ETL过程中的应用,还提供了实用的代码示例和性能对比分析。
|
9天前
|
Go 定位技术 索引
Go 语言Map(集合) | 19
Go 语言Map(集合) | 19
|
9天前
|
Go
go语言注释,标识符 | 17
go语言注释,标识符 | 17
|
8天前
|
NoSQL Go API
go语言操作Redis
go语言操作Redis
下一篇
无影云桌面