Зачем в Go нужны методы, если есть функции?
Что проверяют
Понимаете ли вы, что метод в Go это функция с получателем, и что его настоящая ценность в интерфейсах, а не в «ООП как в Java». Следом почти всегда спрашивают про получатель по значению и по указателю.
Как отвечать
Технически func (r Rectangle) Area() float64 почти то же, что функция с первым параметром: вызов r.Area() можно записать как Rectangle.Area(r). Разница в том, что метод входит в набор методов типа, а от этого зависит всё остальное:
- интерфейсы: тип реализует интерфейс неявно, если у него есть нужные методы. Функцией удовлетворить
io.Reader,fmt.Stringer,errorилиhttp.Handlerнельзя, а на этом полиморфизме держится вся стандартная библиотека; - пространство имён:
Area()может быть и уRectangle, и уCircle, вместоRectangleAreaиCircleAreaв одном пакете; - встраивание: методы встроенного типа продвигаются во внешний, так поведение собирают композицией вместо наследования;
- значения-методы:
f := r.Areaдаёт функцию, привязанную к конкретномуr, её удобно передавать как колбэк.
Про получатели: метод с получателем *T может менять состояние и не копирует большую структуру, с получателем T работает с копией. Набор методов T включает только методы с получателем по значению, у *T есть все. Поэтому если Write объявлен на *Buffer, интерфейс реализует *Buffer, а не Buffer. Хорошая практика: не смешивать виды получателей в одном типе.
Если поведению не нужно состояние типа и не нужен интерфейс, обычная функция в Go нормальна и идиоматична.
Где спотыкаются
Говорят, что методы дают инкапсуляцию. В Go она на уровне пакета: доступ определяет регистр первой буквы имени, методы тут ни при чём.
Путаются в наборах методов и не могут объяснить ошибку компиляции вида «does not implement Writer (method Write has pointer receiver)».
Пытаются объявить метод на int или на типе из чужого пакета. Так нельзя, сначала объявляют свой тип: type Celsius float64.