> Что такое полиморфизм в Go (Go)

Уровень: junior · Роль: backend · Язык: Go · Категория: Технические вопросы

Компании: Лига Цифровой Экономики

Стек: Go

> Пример ответа

Короткий ответ

Полиморфизм в Go реализуется через интерфейсы. В отличие от классического наследования, Go использует структурную типизацию: тип автоматически удовлетворяет интерфейсу, если реализует все его методы. Это позволяет писать код, работающий с разными типами через общий контракт, не связывая их иерархией наследования. Интерфейсы в Go - это набор методов, а не объявление явной связи между типами.

Подробное объяснение

В Go нет классов и наследования в классическом понимании. Вместо этого полиморфизм достигается через интерфейсы - абстрактные типы, описывающие поведение через набор методов. Ключевая особенность: интерфейс удовлетворяется неявно. Если тип реализует все методы интерфейса, он автоматически становится его реализацией, даже если не объявлял об этом явно.

Интерфейсы бывают двух видов:

  • статические - с одним или несколькими методами, проверяются на этапе компиляции;
  • пустые (interface{} или any) - без методов, принимают любой тип, используются для работы с неизвестными значениями.

Полиморфизм проявляется в двух формах:

  • параметрический - через дженерики (Go 1.18+), когда функция работает с разными типами через type parameters;
  • ad-hoc - через интерфейсы, когда один и тот же код вызывает методы разных типов через общий интерфейсный тип.

Важно понимать: интерфейс в Go - это пара "тип + значение" (type descriptor и data pointer). Когда переменная интерфейсного типа содержит конкретное значение, вызов метода происходит через динамическую диспетчеризацию.

На практике

На практике полиморфизм через интерфейсы используется повсеместно:

  • стандартная библиотека: io.Reader, io.Writer, fmt.Stringer, error;
  • внедрение зависимостей: функции принимают интерфейсы, а не конкретные типы;
  • тестирование: моки реализуют те же интерфейсы, что и реальные зависимости;
  • обработка ошибок: любой тип с методом Error() string автоматически становится error.

Правило "принимай интерфейсы, возвращай конкретные типы" - распространённая практика. Это позволяет легко подменять реализации, не меняя вызывающий код. Дженерики решают задачи, где интерфейсы неудобны - например, когда нужна типобезопасность для разных типов без приведения к any.

Пример кода

GO
package main
import "fmt"
// Интерфейс определяет контракт поведения
type Speaker interface {
Speak() string
}
// Тип Dog реализует Speaker неявно
type Dog struct{}
func (d Dog) Speak() string {
return "Woof!"
}
// Тип Cat тоже реализует Speaker
type Cat struct{}
func (c Cat) Speak() string {
return "Meow!"
}
// Функция принимает интерфейс - работает с любым типом,
// который реализует Speak()
func introduce(s Speaker) {
fmt.Println(s.Speak())
}
func main() {
dog := Dog{}
cat := Cat{}
// Оба типа передаются как Speaker
introduce(dog) // Woof!
introduce(cat) // Meow!
}

Как отвечать на собеседовании

Начни с определения: полиморфизм - способность разных типов обрабатывать одинаковые вызовы единообразно. В Go это достигается через интерфейсы, а не наследование. Подчеркни неявную реализацию - это главное отличие от Java или C#. Упомяни, что интерфейсы описывают поведение, а не структуру. Приведи пример с io.Reader или собственным интерфейсом. Если спросят про дженерики - скажи, что они дополняют интерфейсы, но решают другую задачу: интерфейсы - это абстракция поведения, дженерики - абстракция типов. Говори уверенно, без лишних деталей, но покажи понимание внутреннего устройства: интерфейс - это пара "тип + значение".

Что проверяет интервьюер

Интервьюер проверяет:

  • понимание, что в Go нет наследования и полиморфизм реализован иначе;
  • знание неявной реализации интерфейсов;
  • умение объяснить разницу между интерфейсами и дженериками;
  • понимание, когда использовать интерфейсы, а когда конкретные типы;
  • знание стандартных интерфейсов (error, Stringer, Reader/Writer);
  • способность привести корректный пример с реальным кодом.

Типичные ошибки

  • говорить "Go поддерживает наследование" - это неверно;
  • утверждать, что интерфейсы нужно объявлять явно (implements) - в Go это не требуется;
  • путать пустой интерфейс any с полиморфизмом - это лишь способ хранения произвольных значений;
  • считать, что дженерики заменяют интерфейсы - это разные механизмы;
  • забывать, что интерфейс хранит не только значение, но и информацию о типе;
  • приводить примеры из ООП-языков без адаптации к Go.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью