Go語言方法和接收器類型詳解
1. 方法接收器類型
1.1 值接收器
值接收器方法不會改變接收器的狀態,因為Go語言會在調用時復制接收器的值。因此,任何對接收器成員變量的修改都只會影響副本,而不會影響原始結構體實例。
type Person struct {Name stringAge int
}// 值接收器方法
func (p Person) GetAge() int {return p.Age
}func TestValueReceiver(t *testing.T) {p := Person{Name: "Tom", Age: 20}if age := p.GetAge(); age != 20 {t.Errorf("Expected age 20, got %d", age)}// p的值在方法調用后不會改變
}
1.2 指針接收器
指針接收器允許直接操作接收器本身,這意味著可以安全地修改接收器的字段,并且這些更改會持續存在。
// 指針接收器方法
func (p *Person) SetAge(age int) {p.Age = age
}func TestPointerReceiver(t *testing.T) {p := &Person{Name: "Tom", Age: 20}p.SetAge(21)if p.Age != 21 {t.Errorf("Expected age 21, got %d", p.Age)}// p的值會被修改
}
2. 值類型和指針類型的方法調用
2.1 值類型調用指針接收器方法
func TestValueTypeCallPointerMethod(t *testing.T) {p := Person{Name: "Tom", Age: 20}p.SetAge(21) // Go 自動轉換為 (&p).SetAge(21)if p.Age != 21 {t.Errorf("Expected age 21, got %d", p.Age)}
}
2.2 指針類型調用值接收器方法
func TestPointerTypeCallValueMethod(t *testing.T) {p := &Person{Name: "Tom", Age: 20}age := p.GetAge() // Go 自動轉換為 (*p).GetAge()if age != 20 {t.Errorf("Expected age 20, got %d", age)}
}
3. 不可尋址的情況
3.1 Map值不可直接修改
func TestMapValueNotAddressable(t *testing.T) {people := make(map[string]Person)people["tom"] = Person{Name: "Tom", Age: 20}// 錯誤做法// people["tom"].Age = 21// 正確的做法person := people["tom"]person.Age = 21people["tom"] = personif people["tom"].Age != 21 {t.Errorf("Expected age 21, got %d", people["tom"].Age)}
}
3.2 臨時值不可尋址
func TestTemporaryValueNotAddressable(t *testing.T) {// 以下代碼會編譯錯誤// Person{Name: "Tom", Age: 20}.SetAge(21)// 可以調用值接收器方法age := Person{Name: "Tom", Age: 20}.GetAge()if age != 20 {t.Errorf("Expected age 20, got %d", age)}
}
3.3 字面量不可尋址
func TestLiteralNotAddressable(t *testing.T) {// 以下代碼會編譯錯誤// (&struct{ name string }{"tom"}).name = "jerry"// 正確的做法是先賦值給變量p := Person{Name: "Tom", Age: 20}p.SetAge(21)if p.Age != 21 {t.Errorf("Expected age 21, got %d", p.Age)}
}
3.4 Map值是指針類型的情況
func TestMapWithPointerValues(t *testing.T) {// 創建一個字符串到 *Person 的映射people := make(map[string]*Person)// 添加一個新的 Person 到映射中people["tom"] = &Person{Name: "Tom", Age: 20}// 直接修改 tom 的年齡people["tom"].Age = 21if people["tom"].Age != 21 {t.Errorf("Expected age 21, got %d", people["tom"].Age)}
}
4. Map值的正確修改方式
- 獲取值的副本
- 修改副本
- 將修改后的副本存回map
5. 方法調用的自動轉換
5.1 值到指針的自動轉換
- Go編譯器自動處理從值到指針的轉換
- 允許值類型調用指針接收器方法
5.2 指針到值的自動轉換
- Go編譯器自動處理從指針到值的轉換
- 允許指針類型調用值接收器方法
注意事項
-
選擇接收器類型時考慮:
- 是否需要修改接收器狀態
- 性能考慮
- 接口實現要求
-
處理不可尋址情況:
- 使用中間變量
- 正確處理map值的修改
- 注意指針類型的使用
-
使用map存儲指針時:
- 可以直接修改指針指向的對象
- 確保指針正確初始化
- 考慮并發安全問題