第五章:函数和方法 3.方法 --Go 语言轻松入门

在 Go 语言中,方法(Method)是一种特殊的函数,它与特定类型相关联。方法允许你定义一个行为,该行为属于某个类型的实例。这使得面向对象编程风格的一些特性得以实现,尽管 Go 本身并不完全支持传统的面向对象编程概念,如类和继承。

方法的定义

方法的定义与普通函数类似,但有一个关键的区别:方法的第一个参数总是接收者(receiver),这个接收者指定了方法所属的类型。接收者可以是值类型或指针类型。

基本语法

go 复制代码
func (r ReceiverType) methodName(parameters) (results) {
    // 方法体
}
  • ReceiverType 是方法所属的类型。
  • r 是接收者的名称,通常选择简短且有意义的名字。
  • methodName 是方法的名称。
  • parametersresults 分别是方法的参数列表和返回值列表。

示例

假设我们有一个 Person 结构体,并希望为它定义一些方法:

go 复制代码
package main

import "fmt"

// 定义 Person 结构体
type Person struct {
    FirstName string
    LastName  string
}

// 定义一个方法,用于获取全名
func (p Person) FullName() string {
    return p.FirstName + " " + p.LastName
}

// 定义一个方法,用于设置姓氏
func (p *Person) SetLastName(lastName string) {
    p.LastName = lastName
}

func main() {
    // 创建一个 Person 实例
    person := Person{FirstName: "John", LastName: "Doe"}

    // 调用方法
    fmt.Println(person.FullName()) // 输出: John Doe

    // 修改姓氏
    person.SetLastName("Smith")
    fmt.Println(person.FullName()) // 输出: John Smith
}

在这个例子中,FullName 方法是基于值接收者 Person 的方法,而 SetLastName 方法使用了指针接收者 *Person。这是因为 SetLastName 需要修改 Person 结构体中的字段,因此需要通过指针来访问实际的数据。

接收者类型

  • 值接收者 (T):当方法不修改接收者的状态时,可以使用值接收者。这样可以保证方法不会改变原始数据。
  • 指针接收者 (*T):当方法需要修改接收者的状态时,应该使用指针接收者。这样可以直接修改原始数据,避免复制大型结构体带来的性能开销。

方法集

  • 值类型的方法集:包含所有以值类型作为接收者的方法。
go 复制代码
package main

import "fmt"

// 定义一个简单的结构体
type Person struct {
    FirstName string
    LastName  string
}

// 使用值类型接收者的方法
func (p Person) FullName() string {
    return p.FirstName + " " + p.LastName
}

// 使用值类型接收者的方法
func (p Person) Greet() string {
    return "Hello, my name is " + p.FullName()
}

func main() {
    // 创建一个 Person 实例
    person := Person{FirstName: "Alice", LastName: "Smith"}

    // 调用 FullName 方法
    fmt.Println(person.FullName()) // 输出: Alice Smith

    // 调用 Greet 方法
    fmt.Println(person.Greet()) // 输出: Hello, my name is Alice Smith
}
  • 指针类型的方法集:包含所有以指针类型作为接收者的方法,以及所有以值类型作为接收者的方法。
go 复制代码
package main

import "fmt"

// 定义一个简单的结构体
type Person struct {
    FirstName string
    LastName  string
}

// 使用值类型接收者的方法
func (p Person) FullName() string {
    return p.FirstName + " " + p.LastName
}

// 使用指针类型接收者的方法
func (p *Person) SetLastName(lastName string) {
    p.LastName = lastName
}

// 使用指针类型接收者的方法
func (p *Person) Greet() string {
    return "Hello, my name is " + p.FullName()
}

func main() {
    // 创建一个 Person 实例
    person := &Person{FirstName: "Alice", LastName: "Smith"}

    // 调用 FullName 方法(值类型接收者)
    fmt.Println(person.FullName()) // 输出: Alice Smith

    // 调用 SetLastName 方法(指针类型接收者)
    person.SetLastName("Johnson")
    fmt.Println(person.FullName()) // 输出: Alice Johnson

    // 调用 Greet 方法(指针类型接收者)
    fmt.Println(person.Greet()) // 输出: Hello, my name is Alice Johnson
}

这意味着你可以调用一个指向结构体的指针的方法,即使该方法是基于值接收者定义的。但是,你不能直接调用一个基于指针接收者定义的方法,除非你拥有一个指向该结构体的指针。

方法与接口

Go 语言中的接口(Interface)可以通过方法集来实现多态性。如果一个类型实现了接口要求的所有方法,那么这个类型就自动实现了该接口。

go 复制代码
package main

import "fmt"

// Greeter接口定义了一个可以打招呼的对象
type Greeter interface {
	Greet() string
}

// Person结构体表示一个人,包含姓名字段
type Person struct {
	Name string
}

// Greet方法为Person类型实现Greeter接口
func (p Person) Greet() string {
	return "Hello, my name is " + p.Name
}

// greet函数接受一个实现了Greeter接口的对象,并调用其Greet方法
func greet(g Greeter) {
	fmt.Println(g.Greet())
}

func main() {
	person := Person{Name: "Alice"}
	greet(person) // 输出: Hello, my name is Alice
}

在这个例子中,Person 类型通过实现 Greet 方法而满足了 Greeter 接口的要求。因此,person 可以被传递给期望 Greeter 类型的函数 greet

总结

方法是 Go 语言中非常重要的概念,它让代码更加模块化和可重用。通过合理地定义方法,你可以为自定义类型添加行为,从而更自然地表达业务逻辑。

相关推荐
小江的记录本1 分钟前
【Transformer架构】Transformer架构核心知识体系(包括自注意力机制、多头注意力、Encoder-Decoder结构)
java·人工智能·后端·python·深度学习·架构·transformer
AIminminHu6 分钟前
OpenGL渲染与几何内核那点事-项目实践理论补充(三-1-(3):番外篇-当你的CAD打开“怪兽级”STL时:从内存爆炸到零拷贝的极致优化)
开发语言·c++·线程·多线程
c++逐梦人15 分钟前
线程同步与互斥
linux·开发语言
zs宝来了21 分钟前
etcd Raft 实现:分布式一致性核心原理
golang·go·后端技术
LucianaiB21 分钟前
【邪修 QClaw】女朋友说我说话太直,我直接用QClaw来解决问题!
后端
疯狂的程序猴22 分钟前
Flutter应用代码混淆完整指南:Android与iOS平台配置详解
后端·ios
坐吃山猪24 分钟前
Python09_正则表达式
开发语言·python·正则表达式
AI科技星28 分钟前
v=c 物理理论核心参数转换表达式大全
开发语言·线性代数·算法·数学建模·平面
gelald31 分钟前
SpringBoot - 配置加载
spring boot·后端·spring
DyLatte32 分钟前
当我想把所有角色都做好时,就开始内耗了
前端·后端·程序员