Что такое полиморфизм в Go
Что проверяют
Понимаете ли вы, что в Go нет классов и наследования, и как язык обходится без них. Вопрос быстро выводит на интерфейсы, а через них на принятый в Go способ проектирования: контракт объявляет тот, кто его потребляет, а не тот, кто реализует.
Как отвечать
Полиморфизм это возможность работать с разными типами через один контракт. В Go он существует в двух формах.
Через интерфейсы: тип реализует интерфейс неявно, самим наличием нужных методов, ничего объявлять дополнительно не требуется. Интерфейс держат узким и объявляют рядом с функцией, которая его принимает.
type Notifier interface {
Notify(ctx context.Context, msg string) error
}
func Broadcast(ctx context.Context, ns []Notifier, msg string) error {
for _, n := range ns {
if err := n.Notify(ctx, msg); err != nil {
return err
}
}
return nil
}Через параметры типов: обобщённая функция с ограничением работает с любым подходящим типом, но тип известен на этапе компиляции.
Разница по механике: вызов через интерфейс разрешается во время выполнения по таблице методов, обобщённый код раскрывается компилятором.
Где спотыкаются
Забывают про набор методов. Если метод объявлен с получателем-указателем, интерфейс реализует только указатель на тип, и присвоить туда значение не выйдет. Мешать в одном типе оба вида получателей не стоит.
Кладут в интерфейс указатель, равный nil, и удивляются, что проверка на nil не срабатывает. Интерфейсное значение это пара из типа и данных, и заполненный тип делает всё значение ненулевым. Возвращать конкретный типизированный указатель в качестве ошибки опасно ровно поэтому.
Тащат иерархии из других языков: объявляют широкий интерфейс на стороне реализации и встраивают структуры, изображая наследование. Встраивание это делегирование, переопределения метода в нём нет, а широкий интерфейс мешает подменять зависимость в тестах.