Bài học theo mô-đun (2/5)
Phương thức: giá trị và bộ nhận con trỏ
phương thức trong Go là một hàm có bộ thu: một
tham số được khai báo giữa func và tên phương thức, liên kết với
phương thức thành một kiểu. Không có lớp: các phương thức có thể được định nghĩa trên
bất kỳ loại tên nào được khai báo trong gói hiện tại.
Cú pháp
type Person struct {
Name string
}
func (p Person) Greet() string {
return "ciao " + p.Name
}
p := Person{Name: "Ada"}
fmt.Println(p.Greet()) // "ciao Ada"Người nhận p Person tạo Greet một phương thức Person. Bên trong
phương thức, p là một biến giống như bất kỳ biến nào khác.
Bộ nhận giá trị và bộ nhận con trỏ
// VALUE receiver: operates on a COPY of the value
func (p Person) NameUpper() string {
return strings.ToUpper(p.Name)
}
// POINTER receiver: operates on the original value, can mutate it
func (p *Person) Rename(n string) {
p.Name = n
}Với bộ thu con trỏ, bạn có thể sửa đổi bộ thu và những thay đổi được hiển thị bên ngoài phương thức. Với một người nhận giá trị, bạn không thể - bạn sẽ chỉ sửa đổi bản sao cục bộ.
p := Person{Name: "Ada"}
p.Rename("Grace")
fmt.Println(p.Name) // "Grace" — change is visibleKhi nào nên sử dụng bộ thu con trỏ
Các quy định thực tế, theo thứ tự ưu tiên:
- Bạn phải sửa đổi bộ thu → bộ thu con trỏ. Bắt buộc.
- Cấu trúc lớn (hàng tá trường, mảng bên trong) → con trỏ máy thu để tránh những bản sao đắt tiền.
- Tính nhất quán: nếu ÍT NHẤT một phương thức trên loại có bộ nhận con trỏ, hãy sử dụng một máy thu con trỏ cho những người khác nữa. Một quy ước rất quan trọng đối với giao diện (Mô-đun 6).
- Các loại "giá trị" nhỏ, không thay đổi (time.Time, số phức, nhỏ structs) → bộ nhận giá trị, dễ đọc hơn.
Các phương thức trên các kiểu không có cấu trúc
Bạn có thể xác định các phương thức trên bất kỳ loại NAMED nào được khai báo trong gói của mình:
type Celsius float64
func (c Celsius) ToFahrenheit() float64 {
return float64(c)*9/5 + 32
}
t := Celsius(20)
fmt.Println(t.ToFahrenheit()) // 68Bạn không thể xác định chúng theo loại từ các gói khác (ví dụ: int, string): bạn phải
đầu tiên tạo một loại tên địa phương.
Hãy thử nó
Thêm phương thức chuỗi Greet() (bộ nhận giá trị) vào Person trả về 'ciao <Name>'.
Hiển thị gợi ý
Người nhận trong ngoặc đơn TRƯỚC tên phương thức.
Giải pháp khả dụng sau 3 lần thử
Thêm phương thức Đổi tên (n chuỗi) với bộ thu POINTER thay đổi p.Name.
Hiển thị gợi ý
Người nhận con trỏ: `(p *Person)`.
Giải pháp khả dụng sau 3 lần thử
Khi nào bạn nên sử dụng bộ thu con trỏ (p *T)?
func (t ?T) M() { ... }Tóm tắt lại
- Phương thức = chức năng với bộ thu:
func (r T) Name(...) ... { ... }. - Bộ nhận giá trị: thao tác trên một bản sao; Bộ thu con trỏ: hoạt động trên bản gốc.
- Go thực hiện chuyển đổi
T↔*Ttự động tại địa điểm cuộc gọi. - Con trỏ nhận khi: đột biến + cấu trúc lớn + nhất quán với các phương thức khác cùng loại.
- Không bao giờ trộn lẫn bộ nhận giá trị và con trỏ trên cùng một loại.
- Các phương thức thuộc loại NAMED trong gói hiện tại (bao gồm cả non-struct).