Уроки модуля (2/5)
Методы: значение и приемник указателя
Метод в Go — это функция с приемником: специальным
параметр, объявленный между func и именем метода, который связывает
метод к типу. Классов нет: методы можно определить на
любой именованный тип, объявленный в текущем пакете.
Синтаксис
type Person struct {
Name string
}
func (p Person) Greet() string {
return "ciao " + p.Name
}
p := Person{Name: "Ada"}
fmt.Println(p.Greet()) // "ciao Ada"Получатель p Person превращает Greet в метод Person. Внутри
метода, p — это переменная, как и любая другая.
Получатель значений и приемник указателей
// 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
}С помощью приемника указателя вы можете модифицировать приемник и вносить изменения. видны вне метода. С получателем значений вы не можете — вы бы изменяйте только локальную копию.
p := Person{Name: "Ada"}
p.Rename("Grace")
fmt.Println(p.Name) // "Grace" — change is visibleКогда использовать указательный приемник
Практические правила в порядке приоритета:
- Необходимо изменить приемник → приемник указателя. Обязательный.
- Структура большая (десятки полей, массивы внутри) → указатель приемник, чтобы избежать дорогостоящих копий.
- Согласованность: если ПО МИНИМУМ один метод типа имеет приемник указателя, используйте приемник указателя и для остальных тоже. Очень важная конвенция для интерфейсы (Модуль 6).
- Маленькие, неизменяемые, «значительные» типы (время.Время, комплексные числа, небольшие структуры) → приемник значений, более читабельный.
Методы для неструктурных типов
Вы можете определить методы для любого типа NAMED, объявленного в вашем пакете:
type Celsius float64
func (c Celsius) ToFahrenheit() float64 {
return float64(c)*9/5 + 32
}
t := Celsius(20)
fmt.Println(t.ToFahrenheit()) // 68Вы не можете определить их для типов из других пакетов (например, int, string): вы должны
сначала создайте локальный именованный тип.
Попробуйте это
Добавьте к Person строковый метод Greet() (приемник значений), который возвращает «ciao <Name>».
Показать подсказку
Получатель в скобках ПЕРЕД именем метода.
Решение доступно после 3 попыток
Добавьте метод Rename(n string) с приемником POINTER, который изменяет p.Name.
Показать подсказку
Приемник указателя: `(p *Person)`.
Решение доступно после 3 попыток
Когда следует использовать приемник указателя (p *T)?
func (t ?T) M() { ... }Резюме
- Метод = функция с получателем:
func (r T) Name(...) ... { ... }. - Получатель значений: работает с копией; Приемник указателя: работает по оригиналу.
- Go автоматически выполняет преобразование
T↔*Tна месте вызова. — Приемник указателя при: мутации + большой структуре + согласованности с другими методами типа. - Никогда не смешивайте приемники значений и указателей одного типа. — Методы относятся к типам NAMED в текущем пакете (включая неструктурные).