Методы
В прошлом уроке структура была анкетой с подписанными графами. Теперь научим анкету действовать. Представьте, что внизу бланка «Прямоугольник» напечатано: «Площадь: умножьте ширину на высоту». Кто держит заполненный бланк, тот сразу знает, как посчитать его площадь. Действие привязано к виду бланка, а не лежит в общем справочнике.
Посмотрим, зачем это нужно. Площадь есть и у прямоугольника, и у квадрата. Но две функции с именем area в одном пакете объявить нельзя, даже с разными типами параметров:
package main
type Rect struct{ Width, Height float64 }
func area(r Rect) float64 { return r.Width * r.Height }
type Square struct{ Side float64 }
func area(s Square) float64 { return s.Side * s.Side } // ✗
func main() {}area redeclared in this blockМожно назвать функции rectArea и squareArea, но тогда имя каждой операции придётся дополнять именем типа. В Go операцию привязывают к самому типу.
Объявление метода
Метод — функция с дополнительным параметром, записанным в скобках перед именем. Этот параметр называют получателем. Запись func (r Rect) Area() float64 читается слева направо:
func— ключевое слово, как у обычной функции;(r Rect)— получатель: имяrи типRect;Area— имя метода;()— обычные параметры, здесь их нет;float64— тип результата.
Вот методы Area у обоих типов:
type Rect struct{ Width, Height float64 }
func (r Rect) Area() float64 { return r.Width * r.Height }
type Square struct{ Side float64 }
func (s Square) Area() float64 { return s.Side * s.Side }Имя метода принадлежит типу, поэтому Rect.Area и Square.Area не конфликтуют. Методы объявляют вне фигурных скобок структуры, в любом месте пакета. Имя получателя обычно короткое — первая буква типа: r для Rect, s для Square.
Здесь картинка с бланком неточна: инструкция не напечатана внутри структуры. Внутри struct { … } перечисляют только поля, а метод с типом связывает лишь получатель.
Вызов метода
Метод вызывают через точку после значения: r.Area(). Значение перед точкой становится получателем, а внутри метода r — обычный параметр. Вызов r.Area() делает то же, что делала бы функция area(r).
r := Rect{Width: 2, Height: 3}
s := Square{Side: 4}
fmt.Println(r.Area(), s.Area()) // 6 16Давайте разберём вызовы:
- В
r.Area()получателем становитсяr, и метод возвращает2 * 3, то есть6. - В
s.Area()Go вызывает уже методSquare, и он возвращает4 * 4, то есть16. fmt.Printlnпечатает оба числа:6 16.
Go выбирает метод по типу значения перед точкой.
Метод и функция
Этот вопрос часто задают на собеседовании: «Чем метод отличается от функции?» Ответ собирается из трёх пунктов:
- метод — та же функция, но с получателем перед именем;
- получатель привязывает метод к типу, и метод вызывают через точку:
r.Area(); - функцию без получателя вызывают по имени, без значения перед точкой.
Каким типам добавляют методы
Получателем может быть любой тип, объявленный через type в этом же пакете. Вот два частых случая.
Структура
Чаще всего получатель — структура, как Rect и Square выше. Метод читает её поля через получатель: r.Width, r.Height.
Свой тип на основе другого
Подходит и тип вроде type Meters float64. Он хранит такое же число, как float64, но это уже отдельный тип вашего пакета. Поэтому у него могут быть свои методы, например func (m Meters) Km() float64.
Так добавляют действия к числу или строке, не заводя структуру с одним полем. Но метод добавляют только к своему типу: к int или к типу из чужого пакета его не добавить.
Частая ошибка
Метод пытаются объявить у типа int, объявленного в самом языке:
func (n int) Double() int { // ✗
return n * 2
}cannot define new methods on non-local type intПрограмма не компилируется: int объявлен не в вашем пакете. Объявите свой тип на основе int — type Number int — и добавьте метод к нему: func (n Number) Double() Number.
Главное
- Метод — функция с получателем:
func (r Rect) Area() float64. - Метод вызывают через точку,
r.Area(), а значение перед точкой становится получателем. - Методы разных типов могут называться одинаково, а две функции в одном пакете — нет.
- Метод можно добавить только к типу из своего пакета, но не к
int.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.