Chuyển đến nội dung chính
Mô-đun 5 · Bài học 2 trong tổng số 522/50 trong khóa học~14 min
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

Go
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ỏ

Go
// 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ộ.

Go
p := Person{Name: "Ada"}
p.Rename("Grace")
fmt.Println(p.Name)   // "Grace" — change is visible

Khi nào nên sử dụng bộ thu con trỏ

Các quy định thực tế, theo thứ tự ưu tiên:

  1. Bạn phải sửa đổi bộ thu → bộ thu con trỏ. Bắt buộc.
  2. 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.
  3. 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).
  4. 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:

Go
type Celsius float64

func (c Celsius) ToFahrenheit() float64 {
    return float64(c)*9/5 + 32
}

t := Celsius(20)
fmt.Println(t.ToFahrenheit())  // 68

Bạ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ó

tập thể dục#go.m5.l2.e1
Nỗ lực: 0Đang tải…

Thêm phương thức chuỗi Greet() (bộ nhận giá trị) vào Person trả về 'ciao <Name>'.

Đang tải trình chỉnh sửa…
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ử

tập thể dục#go.m5.l2.e2
Nỗ lực: 0Đang tải…

Thêm phương thức Đổi tên (n chuỗi) với bộ thu POINTER thay đổi p.Name.

Đang tải trình chỉnh sửa…
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ử

Quiz#go.m5.l2.e3
Sẵn sàng

Khi nào bạn nên sử dụng bộ thu con trỏ (p *T)?

Go
func (t ?T) M() { ... }
Tùy chọn trả lời

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 ↔ *T tự độ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).