[TOC]
Go語言中的方法(Method)是一種作用于特定類型變量的函數。這種特定類型變量叫做接收者(Receiver)。接收者的概念就類似于 Python 中的 self 。
方法的定義格式如下:
```go
func (接收者變量 接收者類型) 方法名(參數列表) (返回參數) {
函數體
}
```
>[info] 說明:
> 1. 接收者變量:接收者中的參數變量名在命名時,官方建議使用接收者類型名稱首字母的小寫,而不是self、this之類的命名。例如,Student 類型的接收者變量應該命名為 S。
> 2. 接收者類型:接收者類型和參數類似,可以是指針類型和非指針類型。
> 3. 方法名、參數列表、返回參數:具體格式與函數定義相同。
## 值類型的接收者
當方法作用于值類型接收者時,Go語言會在代碼運行時將接收者的值復制一份。在值類型接收者的方法中可以獲取接收者的成員值,但修改操作只是針對副本,無法修改接收者變量本身。
```go
package main
import "fmt"
// Person 是人的結構體
type Person struct {
name string
age int8
}
// NewPerson 是 Person 結構體的構造函數
func NewPerson(name string, age int8) *Person {
return &Person{name: name, age: age}
}
// Dream 是 Person 結構體的夢想方法
func (p Person) Dream(dream string) {
fmt.Printf("%s 的夢想是%s\n", p.name, dream)
}
func main() {
// 構造結構體
p1 := NewPerson("jiaxzeng", 18)
// 調用Person的Dream的方法
p1.Dream("成為大富翁")
p2 := NewPerson("jiaxzeng", 18)
p2.SetAge()
fmt.Printf("p2.age: %v\n", p2.age)
}
// 運行結果
// jiaxzeng 的夢想是成為大富翁
```
## 指針類型的接收者
指針類型的接收者由一個結構體的指針組成,由于指針的特性,調用方法時修改接收者指針的任意成員變量,在方法結束后,修改都是有效的。
```go
package main
import "fmt"
// Person 是人的結構體
type Person struct {
name string
age int8
}
// NewPerson 是 Person 結構體的構造函數
func NewPerson(name string, age int8) *Person {
return &Person{name: name, age: age}
}
// SetAge 是 Person 結構體的修改年齡方法
func (p *Person) SetAge() {
p.age++
}
func main() {
// 構造結構體
p2 := NewPerson("jiaxzeng", 18)
fmt.Printf("p2.age: %v\n", p2.age)
// 調用Person的SetAge的方法
p2.SetAge()
fmt.Printf("p2.age: %v\n", p2.age)
}
// 運行結果
// p2.age: 18
// p2.age: 19
```
## 指針類型接收者使用場景
1. 需要修改接收者中的值
2. 接收者是拷貝代價比較大的大對象
3. 保證一致性,如果有某個方法使用了指針接收者,那么其他的方法也應該使用指針接收者。
## 注意事項
因為slice和map這兩種數據類型都包含了指向底層數據的指針,因此我們在需要復制它們時要特別注意。我們來看下面的例子:
```go
// Person 是人的結構體
type Person struct {
name string
age int8
dreams []string
}
// NewPerson 是 Person 結構體的構造函數
func NewPerson(name string, age int8) *Person {
return &Person{name: name, age: age}
}
// SetDream 是修改 Person 結構體的夢想方法
func (p *Person) SetDream(dreams []string) {
p.dreams = dreams
}
// SetAge 是 Person 結構體的修改年齡方法
func (p *Person) SetAge() {
p.age++
}
func main() {
p1 := NewPerson("jiaxzeng", 18)
dreams := []string{"吃飯", "睡覺"}
p1.SetDream(dreams)
fmt.Printf("p1: %v\n", p1)
dreams[1] = "玩耍"
fmt.Printf("p1: %v\n", p1)
}
// 運行結果
// p1: &{jiaxzeng 18 [吃飯 睡覺]}
// p1: &{jiaxzeng 18 [吃飯 玩耍]}
```
>[danger] 上面的允許結果,修改 dreams 變量的值影響到結構體的值。這個是不允許出現的情況。原因是處在切片是引用類型,結構體方法傳遞的是變量指針。
應該使用以下方式設置。copy函數是返回一個新的切片,所以修改原來的切片不影響結構體。
```go
func (p *Person) SetDream(dreams []string) {
// p.dreams = dreams
p.dreams = make([]string, len(dreams))
copy(p.dreams, dreams)
}
```
>[info] 同樣的問題也存在于返回值slice和map的情況,在實際編碼過程中一定要注意這個問題。
- Golang簡介
- 開發環境
- Golang安裝
- 編輯器及快捷鍵
- vscode插件
- 第一個程序
- 基礎數據類型
- 變量及匿名變量
- 常量與iota
- 整型與浮點型
- 復數與布爾值
- 字符串
- 運算符
- 算術運算符
- 關系運算符
- 邏輯運算符
- 位運算符
- 賦值運算符
- 流程控制語句
- 獲取用戶輸入
- if分支語句
- for循環語句
- switch語句
- break_continue_goto語法
- 高階數據類型
- pointer指針
- array數組
- slice切片
- slice切片擴展
- map映射
- 函數
- 函數定義和調用
- 函數參數
- 函數返回值
- 作用域
- 函數形參傳遞
- 匿名函數
- 高階函數
- 閉包
- defer語句
- 內置函數
- fmt
- strconv
- strings
- time
- os
- io
- 文件操作
- 編碼
- 字符與字節
- 字符串
- 讀寫文件
- 結構體
- 類型別名和自定義類型
- 結構體聲明
- 結構體實例化
- 模擬構造函數
- 方法接收器
- 匿名字段
- 嵌套與繼承
- 序列化
- 接口
- 接口類型
- 值接收者和指針接收者
- 類型與接口對應關系
- 空接口
- 接口值
- 類型斷言
- 并發編程
- 基本概念
- goroutine
- channel
- select
- 并發安全
- 練習題
- 第三方庫
- Survey
- cobra